mirror of https://github.com/fluxcd/flux2.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
9.6 KiB
Go
272 lines
9.6 KiB
Go
//go:build unit
|
|
// +build unit
|
|
|
|
/*
|
|
Copyright 2021 The Flux 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 main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/fluxcd/pkg/apis/meta"
|
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
|
)
|
|
|
|
var pollInterval = 50 * time.Millisecond
|
|
var testTimeout = 10 * time.Second
|
|
|
|
// Update the GitRepository once created to exercise test specific behavior
|
|
type reconcileFunc func(repo *sourcev1.GitRepository)
|
|
|
|
// reconciler waits for an object to be created, then invokes a test supplied
|
|
// function to mutate that object, simulating a controller.
|
|
// Test should invoke run() to run the background reconciler task which
|
|
// polls to wait for the object to exist before applying the update function.
|
|
// Any errors from the reconciler are asserted on test completion.
|
|
type reconciler struct {
|
|
client client.Client
|
|
name types.NamespacedName
|
|
reconcile reconcileFunc
|
|
}
|
|
|
|
// Start the background task that waits for the object to exist then applies
|
|
// the update function.
|
|
func (r *reconciler) run(t *testing.T) {
|
|
result := make(chan error)
|
|
go func() {
|
|
defer close(result)
|
|
err := wait.PollImmediate(
|
|
pollInterval,
|
|
testTimeout,
|
|
r.conditionFunc)
|
|
result <- err
|
|
}()
|
|
t.Cleanup(func() {
|
|
if err := <-result; err != nil {
|
|
t.Errorf("Failure from test reconciler: '%v':", err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
// A ConditionFunction that waits for the named GitRepository to be created,
|
|
// then sets the ready condition to true.
|
|
func (r *reconciler) conditionFunc() (bool, error) {
|
|
var repo sourcev1.GitRepository
|
|
if err := r.client.Get(context.Background(), r.name, &repo); err != nil {
|
|
if errors.IsNotFound(err) {
|
|
return false, nil // Keep polling until object is created
|
|
}
|
|
return true, err
|
|
}
|
|
r.reconcile(&repo)
|
|
err := r.client.Status().Update(context.Background(), &repo)
|
|
return true, err
|
|
}
|
|
|
|
func TestCreateSourceGitExport(t *testing.T) {
|
|
var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --ignore-paths .cosign,non-existent-dir/ -n default --interval 1m --export --timeout=" + testTimeout.String()
|
|
|
|
cases := []struct {
|
|
name string
|
|
args string
|
|
assert assertFunc
|
|
}{
|
|
{
|
|
"ExportSucceeded",
|
|
command,
|
|
assertGoldenFile("testdata/create_source_git/export.golden"),
|
|
},
|
|
{
|
|
name: "no args",
|
|
args: "create secret git",
|
|
assert: assertError("name is required"),
|
|
},
|
|
{
|
|
name: "source with commit",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --commit=c88a2f41 --interval=1m0s --export",
|
|
assert: assertGoldenFile("./testdata/create_source_git/source-git-commit.yaml"),
|
|
},
|
|
{
|
|
name: "source with ref name",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --ref-name=refs/heads/main --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-refname.yaml"),
|
|
},
|
|
{
|
|
name: "source with branch name and commit",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --branch=main --commit=c88a2f41 --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-branch-commit.yaml"),
|
|
},
|
|
{
|
|
name: "source with semver",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --tag-semver=v1.01 --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-semver.yaml"),
|
|
},
|
|
{
|
|
name: "source with git tag",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --tag=test --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-tag.yaml"),
|
|
},
|
|
{
|
|
name: "source with git branch",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --branch=test --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-branch.yaml"),
|
|
},
|
|
{
|
|
name: "source with generic provider",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --provider generic --branch=test --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-provider-generic.yaml"),
|
|
},
|
|
{
|
|
name: "source with azure provider",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider azure --branch=test --interval=1m0s --export",
|
|
assert: assertGoldenFile("testdata/create_source_git/source-git-provider-azure.yaml"),
|
|
},
|
|
{
|
|
name: "source with invalid provider",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider dummy --branch=test --interval=1m0s --export",
|
|
assert: assertError("invalid argument \"dummy\" for \"--provider\" flag: source Git provider 'dummy' is not supported, must be one of: generic|azure"),
|
|
},
|
|
{
|
|
name: "source with empty provider",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --provider \"\" --branch=test --interval=1m0s --export",
|
|
assert: assertError("invalid argument \"\" for \"--provider\" flag: no source Git provider given, please specify the Git provider name"),
|
|
},
|
|
{
|
|
name: "source with no provider",
|
|
args: "create source git podinfo --namespace=flux-system --url=https://dev.azure.com/foo/bar/_git/podinfo --branch=test --interval=1m0s --export --provider",
|
|
assert: assertError("flag needs an argument: --provider"),
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
cmd := cmdTestCase{
|
|
args: tc.args,
|
|
assert: tc.assert,
|
|
}
|
|
cmd.runTestCmd(t)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateSourceGit(t *testing.T) {
|
|
// Default command used for multiple tests
|
|
var command = "create source git podinfo --url=https://github.com/stefanprodan/podinfo --branch=master --timeout=" + testTimeout.String()
|
|
|
|
cases := []struct {
|
|
name string
|
|
args string
|
|
assert assertFunc
|
|
reconcile reconcileFunc
|
|
}{
|
|
{
|
|
"NoArgs",
|
|
"create source git",
|
|
assertError("name is required"),
|
|
nil,
|
|
}, {
|
|
"Succeeded",
|
|
command,
|
|
assertGoldenFile("testdata/create_source_git/success.golden"),
|
|
func(repo *sourcev1.GitRepository) {
|
|
newCondition := metav1.Condition{
|
|
Type: meta.ReadyCondition,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: sourcev1.GitOperationSucceedReason,
|
|
Message: "succeeded message",
|
|
ObservedGeneration: repo.GetGeneration(),
|
|
}
|
|
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
|
repo.Status.Artifact = &sourcev1.Artifact{
|
|
Path: "some-path",
|
|
Revision: "v1",
|
|
LastUpdateTime: metav1.Time{
|
|
Time: time.Now(),
|
|
},
|
|
}
|
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
|
},
|
|
}, {
|
|
"Failed",
|
|
command,
|
|
assertError("failed message"),
|
|
func(repo *sourcev1.GitRepository) {
|
|
stalledCondition := metav1.Condition{
|
|
Type: meta.StalledCondition,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: sourcev1.URLInvalidReason,
|
|
Message: "failed message",
|
|
ObservedGeneration: repo.GetGeneration(),
|
|
}
|
|
apimeta.SetStatusCondition(&repo.Status.Conditions, stalledCondition)
|
|
newCondition := metav1.Condition{
|
|
Type: meta.ReadyCondition,
|
|
Status: metav1.ConditionFalse,
|
|
Reason: sourcev1.URLInvalidReason,
|
|
Message: "failed message",
|
|
ObservedGeneration: repo.GetGeneration(),
|
|
}
|
|
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
|
},
|
|
}, {
|
|
"NoArtifact",
|
|
command,
|
|
assertError("GitRepository source reconciliation completed but no artifact was found"),
|
|
func(repo *sourcev1.GitRepository) {
|
|
// Updated with no artifact
|
|
newCondition := metav1.Condition{
|
|
Type: meta.ReadyCondition,
|
|
Status: metav1.ConditionTrue,
|
|
Reason: sourcev1.GitOperationSucceedReason,
|
|
Message: "succeeded message",
|
|
ObservedGeneration: repo.GetGeneration(),
|
|
}
|
|
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ns := allocateNamespace("podinfo")
|
|
setupTestNamespace(ns, t)
|
|
if tc.reconcile != nil {
|
|
r := reconciler{
|
|
client: testEnv.client,
|
|
name: types.NamespacedName{Namespace: ns, Name: "podinfo"},
|
|
reconcile: tc.reconcile,
|
|
}
|
|
r.run(t)
|
|
}
|
|
cmd := cmdTestCase{
|
|
args: tc.args + " -n=" + ns,
|
|
assert: tc.assert,
|
|
}
|
|
cmd.runTestCmd(t)
|
|
})
|
|
}
|
|
}
|