Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration test to run a helm deploy pipeline #160

Merged
merged 8 commits into from
Oct 20, 2018
Merged
174 changes: 174 additions & 0 deletions pkg/reconciler/v1alpha1/pipelinerun/resources/passedconstraint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
Copyright 2018 The Knative Authors

Licensed 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.
*/

package resources

import (
"testing"

"github.com/google/go-cmp/cmp"

"github.com/knative/build-pipeline/pkg/apis/pipeline/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var mytask1 = &v1alpha1.Task{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace",
Name: "mytask1",
},
Spec: v1alpha1.TaskSpec{
Inputs: &v1alpha1.Inputs{
Resources: []v1alpha1.TaskResource{
v1alpha1.TaskResource{
Name: "myresource1",
Type: v1alpha1.PipelineResourceTypeGit,
},
},
},
},
}

var mytask2 = &v1alpha1.Task{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace",
Name: "mytask2",
},
Spec: v1alpha1.TaskSpec{
Inputs: &v1alpha1.Inputs{
Resources: []v1alpha1.TaskResource{
v1alpha1.TaskResource{
Name: "myresource1",
Type: v1alpha1.PipelineResourceTypeGit,
},
},
},
},
}

var mypipelinetasks = []v1alpha1.PipelineTask{{
Name: "mypipelinetask1",
TaskRef: v1alpha1.TaskRef{Name: "mytask1"},
InputSourceBindings: []v1alpha1.SourceBinding{{
Name: "some-name-1",
Key: "myresource1",
ResourceRef: v1alpha1.PipelineResourceRef{
Name: "myresource1",
},
}},
}, {
Name: "mypipelinetask2",
TaskRef: v1alpha1.TaskRef{Name: "mytask2"},
InputSourceBindings: []v1alpha1.SourceBinding{{
Name: "some-name-2",
Key: "myresource1",
ResourceRef: v1alpha1.PipelineResourceRef{
Name: "myresource1",
},
PassedConstraints: []string{"mytask1"},
}},
}}

var mytaskruns = []v1alpha1.TaskRun{{
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace",
Name: "pipelinerun-mytask1",
},
Spec: v1alpha1.TaskRunSpec{},
}, {
ObjectMeta: metav1.ObjectMeta{
Namespace: "namespace",
Name: "pipelinerun-mytask2",
},
Spec: v1alpha1.TaskRunSpec{},
}}

func TestCanTaskRun(t *testing.T) {
tcs := []struct {
name string
state []*PipelineRunTaskRun
canSecondTaskRun bool
}{
{
name: "first-task-not-started",
state: []*PipelineRunTaskRun{{
Task: mytask1,
PipelineTask: &mypipelinetasks[0],
TaskRunName: "pipelinerun-mytask1",
TaskRun: nil,
}, {
Task: mytask2,
PipelineTask: &mypipelinetasks[1],
TaskRunName: "pipelinerun-mytask2",
TaskRun: nil,
}},
canSecondTaskRun: false,
},
{
name: "first-task-running",
state: []*PipelineRunTaskRun{{
Task: mytask1,
PipelineTask: &mypipelinetasks[0],
TaskRunName: "pipelinerun-mytask1",
TaskRun: makeStarted(mytaskruns[0]),
}, {
Task: mytask2,
PipelineTask: &mypipelinetasks[1],
TaskRunName: "pipelinerun-mytask2",
TaskRun: nil,
}},
canSecondTaskRun: false,
},
{
name: "first-task-failed",
state: []*PipelineRunTaskRun{{
Task: mytask1,
PipelineTask: &mypipelinetasks[0],
TaskRunName: "pipelinerun-mytask1",
TaskRun: makeFailed(mytaskruns[0]),
}, {
Task: mytask2,
PipelineTask: &mypipelinetasks[1],
TaskRunName: "pipelinerun-mytask2",
TaskRun: nil,
}},
canSecondTaskRun: false,
},
{
name: "first-task-finished",
state: []*PipelineRunTaskRun{{
Task: mytask1,
PipelineTask: &mypipelinetasks[0],
TaskRunName: "pipelinerun-mytask1",
TaskRun: makeSucceeded(mytaskruns[0]),
}, {
Task: mytask2,
PipelineTask: &mypipelinetasks[1],
TaskRunName: "pipelinerun-mytask2",
TaskRun: nil,
}},
canSecondTaskRun: true,
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
cantaskrun := canTaskRun(&mypipelinetasks[1], tc.state)
if d := cmp.Diff(cantaskrun, tc.canSecondTaskRun); d != "" {
t.Fatalf("Expected second task availability to run should be %t, but different state returned: %s", tc.canSecondTaskRun, d)
}
})
}
}
28 changes: 26 additions & 2 deletions pkg/reconciler/v1alpha1/pipelinerun/resources/pipelinestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func GetNextTask(prName string, state []*PipelineRunTaskRun, logger *zap.Sugared
logger.Infof("TaskRun %s is still running so we shouldn't start more for PipelineRun %s", prtr.TaskRunName, prName)
return nil
}
} else if canTaskRun(prtr.PipelineTask) {
} else if canTaskRun(prtr.PipelineTask, state) {
logger.Infof("TaskRun %s should be started for PipelineRun %s", prtr.TaskRunName, prName)
return prtr
}
Expand All @@ -54,8 +54,32 @@ func GetNextTask(prName string, state []*PipelineRunTaskRun, logger *zap.Sugared
return nil
}

func canTaskRun(pt *v1alpha1.PipelineTask) bool {
func canTaskRun(pt *v1alpha1.PipelineTask, state []*PipelineRunTaskRun) bool {
// Check if Task can run now. Go through all the input constraints
for _, input := range pt.InputSourceBindings {
if len(input.PassedConstraints) > 0 {
for _, constrainingTaskName := range input.PassedConstraints {
for _, prtr := range state {
// the constraining task must have a successful task run to allow this task to run
if prtr.Task.Name == constrainingTaskName {
if prtr.TaskRun == nil {
return false
}
c := prtr.TaskRun.Status.GetCondition(duckv1alpha1.ConditionSucceeded)
if c == nil {
return false
}
switch c.Status {
case corev1.ConditionFalse:
return false
case corev1.ConditionUnknown:
return false
}
}
}
}
}
}
tejal29 marked this conversation as resolved.
Show resolved Hide resolved
return true
}

Expand Down
66 changes: 66 additions & 0 deletions test/build_logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Copyright 2018 The Knative Authors

Licensed 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.
*/

package test

import (
"io/ioutil"
"strings"

"github.com/knative/pkg/test/logging"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)

//CollectBuildLogs will get the build logs for a task run
func CollectBuildLogs(c *clients, buildName, namespace string, logger *logging.BaseLogger) {
b, err := c.BuildClient.Get(buildName, metav1.GetOptions{})
if err != nil {
logger.Infof("Expected there to be a Build with the same name as TaskRun %s but got error: %s", buildName, err)
}
cluster := b.Status.Cluster
if cluster == nil || cluster.PodName == "" {
logger.Infof("Expected build status to have a podname but it didn't!")
}
logs, err := getInitContainerLogsFromPod(c.KubeClient.Kube, cluster.PodName, namespace)
if err != nil {
logger.Infof("Expected there to be logs from build helm-deploy-pipeline-run-helm-deploy %s", err)
}
logger.Infof("build logs %s", logs)
}

func getInitContainerLogsFromPod(c kubernetes.Interface, pod, namespace string) (string, error) {
p, err := c.CoreV1().Pods(namespace).Get(pod, metav1.GetOptions{})
if err != nil {
return "", err
}

sb := strings.Builder{}
for _, initContainer := range p.Spec.InitContainers {
req := c.CoreV1().Pods(namespace).GetLogs(pod, &corev1.PodLogOptions{Follow: true, Container: initContainer.Name})
rc, err := req.Stream()
if err != nil {
return "", err
}
bs, err := ioutil.ReadAll(rc)
if err != nil {
return "", err
}
sb.Write(bs)
}
return sb.String(), nil
}
20 changes: 19 additions & 1 deletion test/crd_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (

const (
interval = 1 * time.Second
timeout = 2 * time.Minute
timeout = 5 * time.Minute
tejal29 marked this conversation as resolved.
Show resolved Hide resolved
)

// WaitForTaskRunState polls the status of the TaskRun called name from client every
Expand Down Expand Up @@ -86,3 +86,21 @@ func WaitForPipelineRunState(c *clients, name string, inState func(r *v1alpha1.P
return inState(r)
})
}

// WaitForServiceExternalIPState polls the status of the a k8s Service called name from client every
// interval until an external ip is assigned indicating it is done, returns an
// error or timeout. desc will be used to name the metric that is emitted to
// track how long it took for name to get into the state checked by inState.
func WaitForServiceExternalIPState(c *clients, namespace, name string, inState func(s *corev1.Service) (bool, error), desc string) error {
metricName := fmt.Sprintf("WaitForServiceExternalIPState/%s/%s", name, desc)
_, span := trace.StartSpan(context.Background(), metricName)
defer span.End()

return wait.PollImmediate(interval, timeout, func() (bool, error) {
r, err := c.KubeClient.Kube.CoreV1().Services(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return true, err
}
return inState(r)
})
}
11 changes: 11 additions & 0 deletions test/gohelloworld/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang

# Copy the local package files to the container's workspace.
ADD . /go/src/github.com/knative/build-pipeline/test/gohelloworld

RUN go install github.com/knative/build-pipeline/test/gohelloworld

ENTRYPOINT /go/bin/gohelloworld

# Document that the service listens on port 8080.
EXPOSE 8080
21 changes: 21 additions & 0 deletions test/gohelloworld/gohelloworld-chart/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
5 changes: 5 additions & 0 deletions test/gohelloworld/gohelloworld-chart/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: gohelloworld-chart
version: 0.1.0
32 changes: 32 additions & 0 deletions test/gohelloworld/gohelloworld-chart/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "gohelloworld-chart.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "gohelloworld-chart.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "gohelloworld-chart.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
Loading