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

GitRepo Initial Checkout when DisablePolling is True #2469

Merged
merged 28 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions e2e/assets/gitrepo/gitrepo-polling-disabled.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: fleet.cattle.io/v1alpha1
kind: GitRepo
metadata:
name: {{ .Name }}
spec:
repo: {{ .Repo }}
branch: {{ .Branch }}
paths:
- disable_polling
targetNamespace: {{ .TargetNamespace }}
disablePolling: true


134 changes: 134 additions & 0 deletions e2e/single-cluster/gitrepo_polling_disabled_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package singlecluster_test

import (
"math/rand"
"os"
"path"

"github.com/go-git/go-git/v5"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"github.com/rancher/fleet/e2e/testenv"
"github.com/rancher/fleet/e2e/testenv/githelper"
"github.com/rancher/fleet/e2e/testenv/kubectl"
)

var _ = Describe("GitRepoPollingDisabled", Label("infra-setup"), func() {
var (
tmpDir string
clonedir string
k kubectl.Command
gh *githelper.Git
clone *git.Repository
repoName string
inClusterRepoURL string
gitrepoName string
r = rand.New(rand.NewSource(GinkgoRandomSeed()))
targetNamespace string
)

BeforeEach(func() {
k = env.Kubectl.Namespace(env.Namespace)
})

JustBeforeEach(func() {
// Build git repo URL reachable _within_ the cluster, for the GitRepo
host, err := githelper.BuildGitHostname(env.Namespace)
Expect(err).ToNot(HaveOccurred())

addr, err := githelper.GetExternalRepoAddr(env, port, repoName)
Expect(err).ToNot(HaveOccurred())
gh = githelper.NewHTTP(addr)

inClusterRepoURL = gh.GetInClusterURL(host, port, repoName)

tmpDir, _ = os.MkdirTemp("", "fleet-")
clonedir = path.Join(tmpDir, repoName)

gitrepoName = testenv.RandomFilename("gitjob-test", r)
})

AfterEach(func() {
_ = os.RemoveAll(tmpDir)

_, err := k.Delete("gitrepo", gitrepoName)
Expect(err).ToNot(HaveOccurred())

Eventually(func() string {
out, _ := k.Get("bundledeployments", "-A")
return out
}).ShouldNot(ContainSubstring(gitrepoName))

out, err := k.Namespace("cattle-fleet-system").Logs(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This could be flagged as a failure if any previous test failed.

"-l",
"app=fleet-controller",
"-c",
"fleet-controller",
)
Expect(err).ToNot(HaveOccurred())
Expect(out).ToNot(ContainSubstring("ERROR"))
})

When("applying a gitrepo with disable polling", func() {
BeforeEach(func() {
repoName = "repo"
targetNamespace = testenv.NewNamespaceName("disable-polling", r)
})

JustBeforeEach(func() {
err := testenv.ApplyTemplate(k, testenv.AssetPath("gitrepo/gitrepo-polling-disabled.yaml"), struct {
Name string
Repo string
Branch string
TargetNamespace string
}{
gitrepoName,
inClusterRepoURL,
gh.Branch,
targetNamespace,
})
Expect(err).ToNot(HaveOccurred())

clone, err = gh.Create(clonedir, testenv.AssetPath("gitrepo/sleeper-chart"), "disable_polling")
Expect(err).ToNot(HaveOccurred())
})

It("deploys the resources initially and updates them while force updating", func() {
By("checking the pod exists")
Eventually(func() string {
out, _ := k.Namespace(targetNamespace).Get("pods")
return out
}).Should(ContainSubstring("sleeper-"))

By("Updating the git repository")
replace(path.Join(clonedir, "disable_polling", "templates", "deployment.yaml"), "name: sleeper", "name: newsleep")

commit, err := gh.Update(clone)
Expect(err).ToNot(HaveOccurred())

By("Verifying the pods aren't updated")
Eventually(func() string {
out, _ := k.Namespace(targetNamespace).Get("pods")
return out
}).ShouldNot(ContainSubstring("newsleep"))

By("Force updating the GitRepo")
patch := `{"spec": {"forceSyncGeneration": 1}}`
out, err := k.Run("patch", "gitrepo", gitrepoName, "--type=merge", "--patch", patch)
Expect(err).ToNot(HaveOccurred(), out)

By("Verifying the pods are updated")
Eventually(func() string {
out, _ := k.Namespace(targetNamespace).Get("pods")
return out
}).Should(ContainSubstring("newsleep"))

By("Verifying the commit hash is updated")
Eventually(func() string {
out, _ := k.Get("gitrepo", gitrepoName, "-o", "jsonpath={.status.commit}")
return out
}).Should(Equal(commit))
})
})
})
64 changes: 61 additions & 3 deletions integrationtests/gitjob/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
)

const (
gitRepoNamespace = "default"
repo = "https://www.github.com/rancher/fleet"
commit = "9ca3a0ad308ed8bffa6602572e2a1343af9c3d2e"
gitRepoNamespace = "default"
repo = "https://www.github.com/rancher/fleet"
commit = "9ca3a0ad308ed8bffa6602572e2a1343af9c3d2e"
stableCommitBranch = "renovate/golang.org-x-crypto-0.x"
stableCommit = "7b4c2b25a2da2160604bde2773ae8aa44ed481dd"
)

var _ = Describe("GitJob controller", func() {
Expand Down Expand Up @@ -268,6 +270,48 @@ var _ = Describe("GitJob controller", func() {
})
})

When("a new GitRepo is created with DisablePolling set to true", func() {
var (
gitRepo v1alpha1.GitRepo
gitRepoName string
job batchv1.Job
)

JustBeforeEach(func() {
gitRepo = createGitRepoWithDisablePolling(gitRepoName)
Expect(k8sClient.Create(ctx, &gitRepo)).To(Succeed())

By("Creating a job")
Eventually(func() error {
jobName := name.SafeConcatName(gitRepoName, name.Hex(repo+stableCommit, 5))
return k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
}).Should(Not(HaveOccurred()))
})

When("a job completes successfully", func() {
BeforeEach(func() {
gitRepoName = "disable-polling"
})

It("updates the commit from the actual repo", func() {
job.Status.Succeeded = 1
job.Status.Conditions = []batchv1.JobCondition{
{
Type: "Complete",
Status: "True",
},
}
Expect(k8sClient.Status().Update(ctx, &job)).ToNot(HaveOccurred())

By("verifying the commit is updated")
Eventually(func() string {
Expect(k8sClient.Get(ctx, types.NamespacedName{Name: gitRepoName, Namespace: gitRepoNamespace}, &gitRepo)).To(Succeed())
return gitRepo.Status.Commit
}, "30s", "1s").Should(Equal(stableCommit))
})
})
})

When("creating a gitRepo that references a nonexistent helm secret", func() {
var (
gitRepo v1alpha1.GitRepo
Expand Down Expand Up @@ -424,3 +468,17 @@ func createGitRepo(gitRepoName string) v1alpha1.GitRepo {
},
}
}

func createGitRepoWithDisablePolling(gitRepoName string) v1alpha1.GitRepo {
return v1alpha1.GitRepo{
ObjectMeta: metav1.ObjectMeta{
Name: gitRepoName,
Namespace: gitRepoNamespace,
},
Spec: v1alpha1.GitRepoSpec{
Repo: repo,
DisablePolling: true,
Branch: stableCommitBranch,
},
}
}
48 changes: 36 additions & 12 deletions internal/cmd/controller/gitops/reconciler/gitjob_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/rancher/fleet/internal/cmd/controller/grutil"
v1alpha1 "github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
"github.com/rancher/fleet/pkg/git"

"github.com/rancher/wrangler/v2/pkg/condition"
"github.com/rancher/wrangler/v2/pkg/kstatus"
Expand Down Expand Up @@ -109,19 +110,30 @@ func (r *GitJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return ctrl.Result{}, fmt.Errorf("error retrieving git job: %w", err)
}

if errors.IsNotFound(err) && gitrepo.Status.Commit != "" {
if err := r.validateExternalSecretExist(ctx, gitrepo); err != nil {
return ctrl.Result{}, grutil.UpdateErrorStatus(ctx, r.Client, req.NamespacedName, gitrepo.Status, err)
}
logger.V(1).Info("Creating Git job resources")
if err := r.createJobRBAC(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to create RBAC resources for git job: %w", err)
}
if err := r.createTargetsConfigMap(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to create targets config map for git job: %w", err)
if errors.IsNotFound(err) {
if gitrepo.Spec.DisablePolling {
if err := r.updateCommit(ctx, gitrepo); err != nil {
if errors.IsConflict(err) {
logger.V(1).Info("conflict updating commit, retrying", "message", err)
return ctrl.Result{Requeue: true}, nil // just retry, but don't show an error
}
return ctrl.Result{}, fmt.Errorf("error updating commit: %v", err)
}
}
if err := r.createJob(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating git job: %w", err)
if gitrepo.Status.Commit != "" {
if err := r.validateExternalSecretExist(ctx, gitrepo); err != nil {
return ctrl.Result{}, grutil.UpdateErrorStatus(ctx, r.Client, req.NamespacedName, gitrepo.Status, err)
}
logger.V(1).Info("Creating Git job resources")
if err := r.createJobRBAC(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to create RBAC resources for git job: %w", err)
}
if err := r.createTargetsConfigMap(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("failed to create targets config map for git job: %w", err)
}
if err := r.createJob(ctx, gitrepo); err != nil {
return ctrl.Result{}, fmt.Errorf("error creating git job: %w", err)
}
}
} else if gitrepo.Status.Commit != "" {
if err = r.deleteJobIfNeeded(ctx, gitrepo, &job); err != nil {
Expand All @@ -140,6 +152,18 @@ func (r *GitJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return ctrl.Result{}, nil
}

func (r *GitJobReconciler) updateCommit(ctx context.Context, gitRepo *v1alpha1.GitRepo) error {
fetcher := git.NewFetch()
commit, err := fetcher.LatestCommit(ctx, gitRepo, r.Client)
if err != nil {
return err
}
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
gitRepo.Status.Commit = commit
return r.Status().Update(ctx, gitRepo)
})
}

func generationOrCommitChangedPredicate() predicate.Predicate {
return predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
Expand Down
4 changes: 4 additions & 0 deletions pkg/git/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ const (

type Fetch struct{}

func NewFetch() *Fetch {
return &Fetch{}
}

func (f *Fetch) LatestCommit(ctx context.Context, gitrepo *v1alpha1.GitRepo, client client.Client) (string, error) {
secretName := DefaultSecretName
if gitrepo.Spec.ClientSecretName != "" {
Expand Down
Loading