Skip to content

Commit

Permalink
Add test cases
Browse files Browse the repository at this point in the history
- Adding a set of integration tests for the use cases when we watch
  for secret events.
- Extend catalog.go with some helper functions
- Add required samples

Signed-off-by: Zoe <[email protected]>
  • Loading branch information
qu1queee committed Nov 30, 2020
1 parent 248f347 commit e6a0651
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 3 deletions.
16 changes: 16 additions & 0 deletions test/build_samples.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ spec:
name: fake-secret
`

// BuildWithOutputRefSecret ...
const BuildWithOutputRefSecret = `
apiVersion: build.dev/v1alpha1
kind: Build
spec:
source:
url: "https://github.com/sbose78/taxi"
strategy:
kind: ClusterBuildStrategy
output:
image: image-registry.openshift-image-registry.svc:5000/example/buildpacks-app
credentials:
name: foo-secret
timeout: 5s
`

// BuildCBSWithShortTimeOut defines a Build with a
// ClusterBuildStrategy and a short timeout
const BuildCBSWithShortTimeOut = `
Expand Down
23 changes: 22 additions & 1 deletion test/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ import (
// Catalog allows you to access helper functions
type Catalog struct{}

// SecretWithAnnotation gives you a secret with build annotation
func (c *Catalog) SecretWithAnnotation(name string, ns string) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
Annotations: map[string]string{build.AnnotationBuildRefSecret: "true"},
},
}
}

// SecretWithoutAnnotation gives you a secret without build annotation
func (c *Catalog) SecretWithoutAnnotation(name string, ns string) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
}
}

// BuildWithClusterBuildStrategy gives you an specific Build CRD
func (c *Catalog) BuildWithClusterBuildStrategy(name string, ns string, strategyName string, secretName string) *build.Build {
buildStrategy := build.ClusterBuildStrategyKind
Expand Down Expand Up @@ -177,7 +198,7 @@ func (c *Catalog) FakeSecretList() corev1.SecretList {
}
}

// FakeSecretListInNamespace to support test
// FakeNoSecretListInNamespace to support test
func (c *Catalog) FakeNoSecretListInNamespace() corev1.SecretList {
return corev1.SecretList{
Items: []corev1.Secret{},
Expand Down
204 changes: 204 additions & 0 deletions test/integration/build_to_secrets_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// Copyright The Shipwright Contributors
//
// SPDX-License-Identifier: Apache-2.0
package integration_test

import (
"fmt"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
"github.com/shipwright-io/build/test"
corev1 "k8s.io/api/core/v1"
)

var _ = Describe("Integration tests Build and referenced Secrets", func() {

var (
cbsObject *v1alpha1.ClusterBuildStrategy
buildObject *v1alpha1.Build
)
// Load the ClusterBuildStrategies before each test case
BeforeEach(func() {
cbsObject, err = tb.Catalog.LoadCBSWithName(STRATEGY+tb.Namespace, []byte(test.ClusterBuildStrategySingleStep))
Expect(err).To(BeNil())

err = tb.CreateClusterBuildStrategy(cbsObject)
Expect(err).To(BeNil())
})

// Delete the ClusterBuildStrategies after each test case
AfterEach(func() {
err := tb.DeleteClusterBuildStrategy(cbsObject.Name)
Expect(err).To(BeNil())
})

Context("when a build reference a secret with annotations for the spec output", func() {
It("should validate the Build after secret deletion", func() {

// populate Build related vars
buildName := BUILD + tb.Namespace
buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(
buildName,
STRATEGY+tb.Namespace,
[]byte(test.BuildWithOutputRefSecret),
)
Expect(err).To(BeNil())

sampleSecret := tb.Catalog.SecretWithAnnotation(buildObject.Spec.Output.SecretRef.Name, buildObject.Namespace)

Expect(tb.CreateSecret(sampleSecret)).To(BeNil())

Expect(tb.CreateBuild(buildObject)).To(BeNil())

// wait until the Build finish the validation
buildObject, err := tb.GetBuildTillValidation(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionTrue))
Expect(buildObject.Status.Reason).To(Equal("Succeeded"))

// delete a secret
Expect(tb.DeleteSecret(buildObject.Spec.Output.SecretRef.Name)).To(BeNil())

// assert that the validation happened one more time
buildObject, err = tb.GetBuildTillRegistration(buildName, corev1.ConditionFalse)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionFalse))
Expect(buildObject.Status.Reason).To(Equal(fmt.Sprintf("secret %s does not exist", buildObject.Spec.Output.SecretRef.Name)))

})

It("should validate when a missing secret is recreated", func() {
// populate Build related vars
buildName := BUILD + tb.Namespace
buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(
buildName,
STRATEGY+tb.Namespace,
[]byte(test.BuildCBSMinimalWithFakeSecret),
)
Expect(err).To(BeNil())

Expect(tb.CreateBuild(buildObject)).To(BeNil())

// wait until the Build finish the validation
buildObject, err := tb.GetBuildTillValidation(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionFalse))
Expect(buildObject.Status.Reason).To(Equal(fmt.Sprintf("secret %s does not exist", "fake-secret")))

sampleSecret := tb.Catalog.SecretWithAnnotation(buildObject.Spec.Output.SecretRef.Name, buildObject.Namespace)

// generate resources
Expect(tb.CreateSecret(sampleSecret)).To(BeNil())

// assert that the validation happened one more time
buildObject, err = tb.GetBuildTillRegistration(buildName, corev1.ConditionTrue)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionTrue))
Expect(buildObject.Status.Reason).To(Equal("Succeeded"))
})
})

Context("when a build reference a secret without annotations for the spec output", func() {
It("should not validate the Build after a secret deletion", func() {

// populate Build related vars
buildName := BUILD + tb.Namespace
buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(
buildName,
STRATEGY+tb.Namespace,
[]byte(test.BuildWithOutputRefSecret),
)
Expect(err).To(BeNil())

sampleSecret := tb.Catalog.SecretWithoutAnnotation(buildObject.Spec.Output.SecretRef.Name, buildObject.Namespace)

// generate resources
Expect(tb.CreateSecret(sampleSecret)).To(BeNil())
Expect(tb.CreateBuild(buildObject)).To(BeNil())

// wait until the Build finish the validation
buildObject, err := tb.GetBuildTillValidation(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionTrue))
Expect(buildObject.Status.Reason).To(Equal("Succeeded"))

// delete a secret
Expect(tb.DeleteSecret(buildObject.Spec.Output.SecretRef.Name)).To(BeNil())

// assert that the validation happened one more time
buildObject, err = tb.GetBuild(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionTrue))
})

It("should not validate when a missing secret is recreated without annotation", func() {
// populate Build related vars
buildName := BUILD + tb.Namespace
buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(
buildName,
STRATEGY+tb.Namespace,
[]byte(test.BuildCBSMinimalWithFakeSecret),
)
Expect(err).To(BeNil())

Expect(tb.CreateBuild(buildObject)).To(BeNil())

// wait until the Build finish the validation
buildObject, err := tb.GetBuildTillValidation(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionFalse))
Expect(buildObject.Status.Reason).To(Equal(fmt.Sprintf("secret %s does not exist", "fake-secret")))

sampleSecret := tb.Catalog.SecretWithoutAnnotation(buildObject.Spec.Output.SecretRef.Name, buildObject.Namespace)

// generate resources
Expect(tb.CreateSecret(sampleSecret)).To(BeNil())

// // assert that the validation happened one more time
buildObject, err = tb.GetBuildTillRegistration(buildName, corev1.ConditionFalse)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionFalse))
Expect(buildObject.Status.Reason).To(Equal(fmt.Sprintf("secret %s does not exist", "fake-secret")))

})

It("should validate when a missing secret is recreated with annotation", func() {
// populate Build related vars
buildName := BUILD + tb.Namespace
buildObject, err = tb.Catalog.LoadBuildWithNameAndStrategy(
buildName,
STRATEGY+tb.Namespace,
[]byte(test.BuildCBSMinimalWithFakeSecret),
)
Expect(err).To(BeNil())

Expect(tb.CreateBuild(buildObject)).To(BeNil())

// wait until the Build finish the validation
buildObject, err := tb.GetBuildTillValidation(buildName)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionFalse))
Expect(buildObject.Status.Reason).To(Equal(fmt.Sprintf("secret %s does not exist", "fake-secret")))

sampleSecret := tb.Catalog.SecretWithoutAnnotation(buildObject.Spec.Output.SecretRef.Name, buildObject.Namespace)

// generate resources
Expect(tb.CreateSecret(sampleSecret)).To(BeNil())

// we modify the annotation so automatic delete does not take place
data := []byte(fmt.Sprintf(`{"metadata":{"annotations":{"%s":"true"}}}`, v1alpha1.AnnotationBuildRefSecret))

_, err = tb.PatchSecret(buildObject.Spec.Output.SecretRef.Name, data)
Expect(err).To(BeNil())

// // assert that the validation happened one more time
buildObject, err = tb.GetBuildTillRegistration(buildName, corev1.ConditionTrue)
Expect(err).To(BeNil())
Expect(buildObject.Status.Registered).To(Equal(corev1.ConditionTrue))
Expect(buildObject.Status.Reason).To(Equal("Succeeded"))

})
})
})
66 changes: 66 additions & 0 deletions test/integration/utils/builds.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ package utils
import (
"context"

corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"

"github.com/shipwright-io/build/pkg/apis/build/v1alpha1"
)
Expand Down Expand Up @@ -54,3 +57,66 @@ func (t *TestBuild) PatchBuildWithPatchType(buildName string, data []byte, pt ty
}
return b, nil
}

// GetBuildTillStartTime ...
func (t *TestBuild) GetBuildTillValidation(name string) (*v1alpha1.Build, error) {

var (
pollBuildTillRegistration = func() (bool, error) {

bInterface := t.BuildClientSet.BuildV1alpha1().Builds(t.Namespace)

buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return false, err
}
// TODO: we might improve the conditional here
if buildRun.Status.Registered != "" {
return true, nil
}

return false, nil
}
)

brInterface := t.BuildClientSet.BuildV1alpha1().Builds(t.Namespace)

err := wait.PollImmediate(t.Interval, t.TimeOut, pollBuildTillRegistration)
if err != nil {
return nil, err
}

return brInterface.Get(context.TODO(), name, metav1.GetOptions{})
}

// GetBuildTillRegistration ...
// TODO: improve func around names and content
func (t *TestBuild) GetBuildTillRegistration(name string, condition corev1.ConditionStatus) (*v1alpha1.Build, error) {

var (
pollBuildTillRegistration = func() (bool, error) {

bInterface := t.BuildClientSet.BuildV1alpha1().Builds(t.Namespace)

buildRun, err := bInterface.Get(context.TODO(), name, metav1.GetOptions{})
if err != nil && !apierrors.IsNotFound(err) {
return false, err
}
// TODO: we might improve the conditional here
if buildRun.Status.Registered == condition {
return true, nil
}

return false, nil
}
)

brInterface := t.BuildClientSet.BuildV1alpha1().Builds(t.Namespace)

err := wait.PollImmediate(t.Interval, t.TimeOut, pollBuildTillRegistration)
if err != nil {
return nil, err
}

return brInterface.Get(context.TODO(), name, metav1.GetOptions{})
}
29 changes: 27 additions & 2 deletions test/integration/utils/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,41 @@ import (

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

// This class is intended to host all CRUD calls for testing secrets primitive resources

// CreateSecret generates a Secret on the current test namespace
func (t *TestBuild) CreateSecret(ns string, secret *corev1.Secret) error {
client := t.Clientset.CoreV1().Secrets(ns)
func (t *TestBuild) CreateSecret(secret *corev1.Secret) error {
client := t.Clientset.CoreV1().Secrets(t.Namespace)
_, err := client.Create(context.TODO(), secret, metav1.CreateOptions{})
if err != nil {
return err
}
return nil
}

// DeleteSecret ...
func (t *TestBuild) DeleteSecret(name string) error {
client := t.Clientset.CoreV1().Secrets(t.Namespace)
if err := client.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil {
return err
}
return nil
}

// PatchSecret ...
func (t *TestBuild) PatchSecret(name string, data []byte) (*corev1.Secret, error) {
return t.PatchSecretWithPatchType(name, data, types.MergePatchType)
}

// PatchSecretWithPatchType ...
func (t *TestBuild) PatchSecretWithPatchType(name string, data []byte, pt types.PatchType) (*corev1.Secret, error) {
secInterface := t.Clientset.CoreV1().Secrets(t.Namespace)
b, err := secInterface.Patch(context.TODO(), name, pt, data, metav1.PatchOptions{})
if err != nil {
return nil, err
}
return b, nil
}

0 comments on commit e6a0651

Please sign in to comment.