Skip to content

Commit

Permalink
Add processTriggerSpec func to wire it to Trigger, and the corresponding
Browse files Browse the repository at this point in the history
unit test.
  • Loading branch information
dorismeixing committed Jul 24, 2020
1 parent 81dc28e commit 5c4b576
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 4 deletions.
104 changes: 101 additions & 3 deletions cmd/triggerRun/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,31 @@ package cmd

import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"

"github.com/spf13/cobra"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
triggersv1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
triggersclientset "github.com/tektoncd/triggers/pkg/client/clientset/versioned"
"github.com/tektoncd/triggers/pkg/template"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/clientcmd"
)

var (
rootCmd = &cobra.Command{
kubeconfig string
rootCmd = &cobra.Command{
Use: "triggers-run",
Short: "Tekton Trigger test",
Short: "This is the CLI for tekton trigger.",
Long: "tkn-trigger will allow users easily test out the Trigger config.",
Run: rootRun,
}

Expand All @@ -43,10 +53,55 @@ var (
func init() {
rootCmd.Flags().StringVarP(&triggerFile, "triggerFile", "t", "", "Path to trigger yaml file")
rootCmd.Flags().StringVarP(&httpPath, "httpPath", "r", "", "Path to body event")
rootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "absolute path to the kubeconfig file")
}

func rootRun(cmd *cobra.Command, args []string) {
// TODO: Not implemented
err := trigger(triggerFile, httpPath)
if err != nil {
fmt.Printf("fail to call trigger: %v", err)
}
}

func trigger(triggerFile, httpPath string) error {
// Read HTTP request.
r, err := readHTTP(httpPath)
if err != nil {
return fmt.Errorf("error reading HTTP file: %w", err)
}
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("error reading HTTP body: %w", err)
}

// Read triggers.
triggers, err := readTrigger(triggerFile)
if err != nil {
return fmt.Errorf("error reading triggers: %w", err)
}

client, err := getKubeClient(kubeconfig)
if err != nil {
return fmt.Errorf("fail to get Kubenetes client: %w", err)
}

logger, _ := zap.NewProduction()
sugerLogger := logger.Sugar()
eventID := template.UID()
for _, tri := range triggers {
eventLog := sugerLogger.With(zap.String(triggersv1.EventIDLabelKey, eventID))
resources, err := processTriggerSpec(client, tri,
r, body, eventID, eventLog)
if err != nil {
return fmt.Errorf("fail to build config from the flags: %w", err)
}
for resource := range resources {
fmt.Print(resource)
}
}

return nil
}

func readTrigger(path string) ([]*v1alpha1.Trigger, error) {
Expand Down Expand Up @@ -87,6 +142,49 @@ func readHTTP(path string) (*http.Request, error) {
return http.ReadRequest(bufio.NewReader(f))
}

func getKubeClient(kubeconfig string) (triggersclientset.Interface, error) {
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, fmt.Errorf("fail to build config from the flags: %w", err)
}

return triggersclientset.NewForConfig(config)
}

func processTriggerSpec(client triggersclientset.Interface, tri *triggersv1.Trigger, request *http.Request, event []byte, eventID string, eventLog *zap.SugaredLogger) ([]json.RawMessage, error) {
if tri == nil {
return nil, errors.New("Trigger is not defined")
}

el, err := triggersv1.ToEventListenerTrigger(tri.Spec)
if err != nil {
return nil, fmt.Errorf("fail to convert Trigger to EvenetListener: %w", err)
}

log := eventLog.With(zap.String(triggersv1.TriggerLabelKey, el.Name))

rt, err := template.ResolveTrigger(el,
client.TriggersV1alpha1().TriggerBindings(tri.Namespace).Get,
client.TriggersV1alpha1().ClusterTriggerBindings().Get,
client.TriggersV1alpha1().TriggerTemplates(tri.Namespace).Get)
if err != nil {
log.Error(err)
return nil, err
}

params, err := template.ResolveParams(rt, event, request.Header)
if err != nil {
log.Error(err)
return nil, err
}

log.Infof("ResolvedParams : %+v", params)
resources := template.ResolveResources(rt.TriggerTemplate, params)

return resources, nil
}

// Execute runs the command.
func Execute() error {
return rootCmd.Execute()
Expand Down
166 changes: 165 additions & 1 deletion cmd/triggerRun/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,26 @@ limitations under the License.
package cmd

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httputil"
"reflect"
"regexp"
"testing"

"github.com/google/go-cmp/cmp"
v1alpha1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
pipelinev1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
triggersv1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1"
triggersclientset "github.com/tektoncd/triggers/pkg/client/clientset/versioned"
"github.com/tektoncd/triggers/test"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
rtesting "knative.dev/pkg/reconciler/testing"
)

func TestReadTrigger(t *testing.T) {
Expand Down Expand Up @@ -82,3 +95,154 @@ X-Header: testheader
t.Errorf("-want +got: %s", diff)
}
}

func Test_processTriggerSpec(t *testing.T) {
type args struct {
t *triggersv1.Trigger
request *http.Request
event []byte
resources test.Resources
}
eventBody := json.RawMessage(`{"repository": {"links": {"clone": [{"href": "testurl", "name": "ssh"}, {"href": "testurl", "name": "http"}]}}, "changes": [{"ref": {"displayId": "test-branch"}}]}`)
r, err := http.NewRequest("POST", "URL", bytes.NewReader(eventBody))
if err != nil {
t.Errorf("Cannot create a new request:%s", err)
}
taskRunTemplate := pipelinev1alpha1.TaskRun{
TypeMeta: metav1.TypeMeta{
APIVersion: "tekton.dev/v1alpha1",
Kind: "TaskRun",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-taskrun",
Namespace: "default",
Labels: map[string]string{
"someLabel": "$(params.foo)",
},
},
Spec: pipelinev1alpha1.TaskRunSpec{
TaskRef: &v1beta1.TaskRef{
Name: "my-task", // non-existent task; just for testing
},
},
}
trBytes, err := json.Marshal(taskRunTemplate)
if err != nil {
t.Fatalf("failed to marshall taskrun to json: %v", err)
}

triggerTemplate := triggersv1.TriggerTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "triggerSpecTemplate",
Namespace: "default",
},
Spec: triggersv1.TriggerTemplateSpec{
Params: []triggersv1.ParamSpec{{
Name: "foo",
}},
ResourceTemplates: []triggersv1.TriggerResourceTemplate{{
RawExtension: runtime.RawExtension{Raw: trBytes},
}},
},
}

triggerBinding := v1alpha1.TriggerBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "triggerSpecBinding1",
Namespace: "default",
},
Spec: triggersv1.TriggerBindingSpec{
Params: []triggersv1.Param{{
Name: "foo",
Value: "bar",
}},
},
}

wantTaskRun := pipelinev1alpha1.TaskRun{
TypeMeta: metav1.TypeMeta{
APIVersion: "tekton.dev/v1alpha1",
Kind: "TaskRun",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-taskrun",
Namespace: "default",
Labels: map[string]string{
"someLabel": "bar", // replaced with the value of foo from bar
},
},
Spec: pipelinev1alpha1.TaskRunSpec{
TaskRef: taskRunTemplate.Spec.TaskRef, // non-existent task; just for testing
},
}
wantTrBytes, err := json.Marshal(wantTaskRun)
if err != nil {
t.Fatalf("failed to marshal wantTaskrun: %v", err)
}

tests := []struct {
name string
args args
want []json.RawMessage
wantErr bool
}{
{
name: "testing-name",
args: args{
t: &v1alpha1.Trigger{
TypeMeta: metav1.TypeMeta{
Kind: "TriggerRun",
APIVersion: "tekton.dev/v1alpha1"},
ObjectMeta: metav1.ObjectMeta{
Name: "my-triggerRun",
},
Spec: v1alpha1.TriggerSpec{
Bindings: []*v1alpha1.TriggerSpecBinding{
{Name: "triggerSpecBinding1"}, // These should be references to TriggerBindings defined below
},
Template: v1alpha1.TriggerSpecTemplate{
Name: "triggerSpecTemplate", // This should be a reference to a TriggerTemplate defined below
},
},
},

request: r,
event: eventBody,
resources: test.Resources{
// Add any resources that we need to create with a fake client
TriggerBindings: []*v1alpha1.TriggerBinding{&triggerBinding},
TriggerTemplates: []*triggersv1.TriggerTemplate{&triggerTemplate},
},
},
want: []json.RawMessage{wantTrBytes},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
eventID := "some-id"
logger, _ := zap.NewProduction()
eventLog := logger.Sugar()
client := getFakeTriggersClient(t, tt.args.resources)
got, err := processTriggerSpec(client, tt.args.t, tt.args.request, tt.args.event, eventID, eventLog)
if (err != nil) != tt.wantErr {
t.Errorf("processTriggerSpec() error = %v. wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("processTriggerSpec() = %v, want %v", got, tt.want)
}

for r := range got {
fmt.Print(r)
}
})
}
}

func getFakeTriggersClient(t *testing.T, resources test.Resources) triggersclientset.Interface {
t.Helper()
ctx, _ := rtesting.SetupFakeContext(t)
clients := test.SeedResources(t, ctx, resources)
return clients.Triggers
}

0 comments on commit 5c4b576

Please sign in to comment.