Skip to content

Commit

Permalink
Fix broken tests (#449)
Browse files Browse the repository at this point in the history
* Refactor tests

* Ensure that program test is parallelisable

Dynamically generate the ConfigMaps with random names so we do not
have spec pollution when we run the tests in parallel.

* Use resetWaitForStack helper function instead

* Make stale state test serialized

* Pin pulumi-kubernetes to v3.26.0 for all tests

There is an issue with foreground deletion propagation. Pin to v3.26.0
until we can figure out what's happening.

* Remove unnecessary print lines
  • Loading branch information
rquitales authored May 11, 2023
1 parent c946ea2 commit 351c30b
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 94 deletions.
1 change: 0 additions & 1 deletion pkg/controller/stack/stack_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1640,7 +1640,6 @@ func (sess *reconcileStackSession) SetupGitAuth(ctx context.Context) (*auto.GitA
if sess.stack.GitAuth != nil {

if sess.stack.GitAuth.SSHAuth != nil {
fmt.Println("sess.stack.GitAuth/SSHAuth: ", sess.stack.GitAuth.SSHAuth)
privateKey, err := sess.resolveResourceRef(ctx, &sess.stack.GitAuth.SSHAuth.SSHPrivateKey)
if err != nil {
return nil, fmt.Errorf("resolving gitAuth SSH private key: %w", err)
Expand Down
94 changes: 68 additions & 26 deletions test/program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,70 @@ import (
pulumiv1 "github.com/pulumi/pulumi-kubernetes-operator/pkg/apis/pulumi/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
yaml "sigs.k8s.io/yaml"
"sigs.k8s.io/yaml"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

// pulumiYamlProgramCMFmt is a template for a YAML program that creates a ConfigMap. Templated
// values are: the name of the ConfigMap, and the value of the `foo` key in the ConfigMap's `data`
const pulumiYamlProgramCMFmt = `
configuration:
foo:
type: "String"
default: "%s"
resources:
provider:
type: pulumi:providers:kubernetes
options:
version: 3.26.0
example:
type: kubernetes:core/v1:ConfigMap
properties:
metadata:
annotations:
pulumi.com/patchForce: "true"
name:
%s
data:
foo: ${foo}
options:
provider: ${provider}
`

// invalidPulumiYamlProgramCM is a YAML program that creates a ConfigMap, but with an invalid Pulumi YAML format.
const invalidPulumiYamlProgramCM = `
resources:
example:
type: kubernetes:core/v1:ConfigMap
properties:
metadata:
annotations:
pulumi.com/patchForce: "true"
name:
%s
data:
foo: bar
options:
provider: ${provider}
`

// generatePulumiYamlCMProgram generates a Pulumi YAML program that creates a ConfigMap. It accepts a base
// template and values to be templated into the base template.
func generatePulumiYamlCMProgram(template string, values ...interface{}) pulumiv1.Program {
prog := pulumiv1.Program{}
prog.Name = randString()
prog.Namespace = "default"

templated := fmt.Sprintf(template, values...)

err := yaml.Unmarshal([]byte(templated), &prog.Program)
ExpectWithOffset(1, err).ToNot(HaveOccurred())

return prog
}

var _ = Describe("Creating a YAML program", func() {
It("is possible to create an empty YAML program", func() {
prog := pulumiv1.Program{}
Expand Down Expand Up @@ -63,6 +121,7 @@ var _ = Describe("Creating a YAML program", func() {

Expect(k8sClient.Create(context.TODO(), &stack)).To(Succeed())
deleteAndWaitForFinalization(&stack)
deleteAndWaitForFinalization(&prog)
})

When("Using a stack", func() {
Expand Down Expand Up @@ -116,7 +175,7 @@ var _ = Describe("Creating a YAML program", func() {
})

It("should fail if given a syntactically correct but invalid program.", func() {
prog := programFromFile("./testdata/test-program-invalid.yaml")
prog := generatePulumiYamlCMProgram(invalidPulumiYamlProgramCM, "cm-"+randString())
Expect(k8sClient.Create(context.TODO(), &prog)).To(Succeed())

stack.Spec.ProgramRef = &shared.ProgramReference{
Expand All @@ -130,46 +189,45 @@ var _ = Describe("Creating a YAML program", func() {
})

It("should run a Program that has been added to a Stack", func() {
prog := programFromFile("./testdata/test-program.yaml")
cmName := "cm-" + randString()
prog := generatePulumiYamlCMProgram(pulumiYamlProgramCMFmt, "bar", cmName)
Expect(k8sClient.Create(context.TODO(), &prog)).To(Succeed())

stack.Spec.ProgramRef = &shared.ProgramReference{
Name: prog.Name,
}

// Set DestroyOnFinalize to clean up the configmap for repeat runs.
stack.Spec.DestroyOnFinalize = true
stack.Name = "ok-program-" + randString()
Expect(k8sClient.Create(context.TODO(), &stack)).To(Succeed())

waitForStackSuccess(&stack)
expectReady(stack.Status.Conditions)

// Check that the ConfigMap was created.
var c corev1.ConfigMap
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: stack.Namespace, Name: "test-configmap"}, &c)).To(Succeed())
Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Namespace: stack.Namespace, Name: cmName}, &c)).To(Succeed())
})

When("the program is changed", func() {
var revisionOnFirstSuccess string

BeforeEach(func() {
prog := programFromFile("./testdata/test-program.yaml")
cmName := "changing-cm-" + randString()
prog := generatePulumiYamlCMProgram(pulumiYamlProgramCMFmt, "bar", cmName)
Expect(k8sClient.Create(context.TODO(), &prog)).To(Succeed())

stack.Spec.ProgramRef = &shared.ProgramReference{
Name: prog.Name,
}

// Set DestroyOnFinalize to clean up the configmap for repeat runs.
stack.Spec.DestroyOnFinalize = true
stack.Name = "changing-program-" + randString()
Expect(k8sClient.Create(context.TODO(), &stack)).To(Succeed())
waitForStackSuccess(&stack)

Expect(stack.Status.LastUpdate).ToNot(BeNil())
revisionOnFirstSuccess = stack.Status.LastUpdate.LastSuccessfulCommit
fmt.Fprintf(GinkgoWriter, ".status.lastUpdate.LastSuccessfulCommit before changing program: %s", revisionOnFirstSuccess)
prog2 := programFromFile("./testdata/test-program-changed.yaml")
prog2 := generatePulumiYamlCMProgram(pulumiYamlProgramCMFmt, "newValue", cmName)
prog.Program = prog2.Program
resetWaitForStack()
Expect(k8sClient.Update(context.TODO(), &prog)).To(Succeed())
Expand All @@ -184,19 +242,3 @@ var _ = Describe("Creating a YAML program", func() {
})
})
})

func programFromFile(path string) pulumiv1.Program {
prog := pulumiv1.Program{}
prog.Name = randString()
prog.Namespace = "default"

programFile, err := os.ReadFile(path)
if err != nil {
fmt.Printf("%+v", err)
Fail(fmt.Sprintf("couldn't read program file: %v", err))
}
err = yaml.Unmarshal(programFile, &prog.Program)
ExpectWithOffset(1, err).ToNot(HaveOccurred())

return prog
}
1 change: 0 additions & 1 deletion test/stack_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ var (
baseDir = ""
)

const namespace = "default"
const pulumiAPISecretName = "pulumi-api-secret"
const pulumiAWSSecretName = "pulumi-aws-secrets"
const k8sOpTimeout = 10 * time.Second
Expand Down
17 changes: 7 additions & 10 deletions test/stale_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"math/rand"
"os"
"path/filepath"
"strings"
"time"

"github.com/pulumi/pulumi-kubernetes-operator/pkg/apis/pulumi/shared"
Expand All @@ -20,7 +19,7 @@ import (
. "github.com/onsi/gomega"
)

var _ = When("a stack uses a provider with credentials kept in state", func() {
var _ = When("a stack uses a provider with credentials kept in state", Ordered, func() {
// This models a situation in which the program constructs a provider using credentials which
// are then rotated. Pulumi has difficulty with this if you want to refresh the state, since the
// provider will be constructed from the credentials in the state, which are out of date. The
Expand Down Expand Up @@ -122,9 +121,7 @@ var _ = When("a stack uses a provider with credentials kept in state", func() {
AfterEach(func() {
deleteAndWaitForFinalization(useRabbitStack)
deleteAndWaitForFinalization(setupStack)
if strings.HasPrefix(tmp, os.TempDir()) {
Expect(os.RemoveAll(tmp)).To(Succeed())
}
os.RemoveAll(tmp)
})

When("the credentials are rotated", func() {
Expand All @@ -140,7 +137,7 @@ var _ = When("a stack uses a provider with credentials kept in state", func() {
"port": rabbitPort,
"secretName": credsSecretName,
}
waitForStackSince = time.Now()
resetWaitForStack()
Expect(k8sClient.Update(ctx, setupStack)).To(Succeed())
waitForStackSuccess(setupStack, "120s")
})
Expand Down Expand Up @@ -209,15 +206,15 @@ var _ = When("a stack uses a provider with credentials kept in state", func() {
"port": rabbitPort,
"secretName": credsSecretName,
}
waitForStackSince = time.Now()
resetWaitForStack()
Expect(k8sClient.Update(ctx, setupStack)).To(Succeed())
waitForStackSuccess(setupStack, "120s")
})

It("fails if the targeted stack isn't run again", func() {
refetch(useRabbitStack) // this is fetched above; but, avoid future coincidences ..
useRabbitStack.Spec.Refresh = true
waitForStackSince = time.Now()
resetWaitForStack()
Expect(k8sClient.Update(context.TODO(), useRabbitStack)).To(Succeed())
time.Sleep(30 * time.Second)

Expand All @@ -237,11 +234,11 @@ var _ = When("a stack uses a provider with credentials kept in state", func() {
{
Name: targetedStack.Name,
Requirement: &shared.RequirementSpec{
SucceededWithinDuration: &metav1.Duration{10 * time.Minute},
SucceededWithinDuration: &metav1.Duration{Duration: 10 * time.Minute},
},
},
}
waitForStackSince = time.Now()
resetWaitForStack()
Expect(k8sClient.Update(ctx, useRabbitStack)).To(Succeed())

// TODO check that it's marked as reconciling pending a prerequisite
Expand Down
9 changes: 6 additions & 3 deletions test/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
Expand All @@ -47,7 +46,9 @@ import (
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.

var cfg *rest.Config
// namespace is the namespace in which the tests are run.
const namespace = "default"

var k8sClient client.Client
var k8sManager ctrl.Manager
var testEnv *envtest.Environment
Expand Down Expand Up @@ -157,9 +158,10 @@ func writeKubeconfig(targetDir string) string {
return kubeconfig
}

// removeTempDir is a convenience for cleaning up after writeKubeconfig.
func removeTempDir(tmp string) {
if strings.HasPrefix(tmp, os.TempDir()) {
os.RemoveAll(tmp)
Expect(os.RemoveAll(tmp)).To(Succeed())
}
}

Expand Down Expand Up @@ -212,6 +214,7 @@ func deleteAndWaitForFinalization(obj client.Object) {
EventuallyWithOffset(1, func() bool {
err := k8sClient.Get(context.TODO(), key, obj)
if err == nil {
// If we can still get the object, it hasn't been finalized yet.
return false
}
ExpectWithOffset(2, client.IgnoreNotFound(err)).To(BeNil())
Expand Down
2 changes: 1 addition & 1 deletion test/testdata/run-rabbitmq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"main": "index.js",
"dependencies": {
"@pulumi/docker": "^3.4.1",
"@pulumi/kubernetes": "^3.0.0",
"@pulumi/kubernetes": "3.26.0",
"@pulumi/pulumi": "^3.0.0",
"@pulumi/random": "^4.8.2"
}
Expand Down
19 changes: 0 additions & 19 deletions test/testdata/test-program-changed.yaml

This file was deleted.

13 changes: 0 additions & 13 deletions test/testdata/test-program-invalid.yaml

This file was deleted.

19 changes: 0 additions & 19 deletions test/testdata/test-program.yaml

This file was deleted.

2 changes: 1 addition & 1 deletion test/testdata/use-rabbitmq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "use-rabbitmq",
"main": "index.js",
"dependencies": {
"@pulumi/kubernetes": "^3.0.0",
"@pulumi/kubernetes": "3.26.0",
"@pulumi/pulumi": "^3.0.0",
"@pulumi/rabbitmq": "^3.2.0"
}
Expand Down

0 comments on commit 351c30b

Please sign in to comment.