From 39b78a8cf967679018fe8e1da6e684155da4d684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Tue, 21 Nov 2023 01:59:55 +0700 Subject: [PATCH] feat(module/git): allow module.git use cached content when failed to fetch (#5712) Signed-off-by: hainenber --- CHANGELOG.md | 2 ++ component/module/git/git.go | 18 +++++++++++++-- component/module/git/internal/vcs/git.go | 28 +++++++++++++++--------- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15df9feae598..d71aeae69605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,8 @@ v0.38.0-rc.0 (2023-11-16) - Updated windows exporter to use prometheus-community/windows_exporter commit 1836cd1. (@mattdurham) +- Allow agent to start with `module.git` config if cached before. (@hainenber) + ### Bugfixes - Set exit code 1 on grafana-agentctl non-runnable command. (@fgouteroux) diff --git a/component/module/git/git.go b/component/module/git/git.go index 42ac468477de..6a211fdc8843 100644 --- a/component/module/git/git.go +++ b/component/module/git/git.go @@ -3,6 +3,7 @@ package git import ( "context" + "errors" "path/filepath" "reflect" "sync" @@ -91,8 +92,15 @@ func New(o component.Options, args Arguments) (*Component, error) { argsChanged: make(chan struct{}, 1), } + // Only acknowledge the error from Update if it's not a + // vcs.UpdateFailedError; vcs.UpdateFailedError means that the Git repo + // exists but we were just unable to update it. if err := c.Update(args); err != nil { - return nil, err + if errors.As(err, &vcs.UpdateFailedError{}) { + level.Error(c.log).Log("msg", "failed to update repository", "err", err) + } else { + return nil, err + } } return c, nil } @@ -193,10 +201,16 @@ func (c *Component) Update(args component.Arguments) (err error) { } // Create or update the repo field. + // Failure to update repository makes the module loader temporarily use cached contents on disk if c.repo == nil || !reflect.DeepEqual(repoOpts, c.repoOpts) { r, err := vcs.NewGitRepo(context.Background(), repoPath, repoOpts) if err != nil { - return err + if errors.As(err, &vcs.UpdateFailedError{}) { + level.Error(c.log).Log("msg", "failed to update repository", "err", err) + c.updateHealth(err) + } else { + return err + } } c.repo = r c.repoOpts = repoOpts diff --git a/component/module/git/internal/vcs/git.go b/component/module/git/internal/vcs/git.go index 8209190b90da..dece43c10b2f 100644 --- a/component/module/git/internal/vcs/git.go +++ b/component/module/git/internal/vcs/git.go @@ -58,16 +58,24 @@ func NewGitRepo(ctx context.Context, storagePath string, opts GitRepoOptions) (* } // Fetch the latest contents. This may be a no-op if we just did a clone. - err = repo.FetchContext(ctx, &git.FetchOptions{ + fetchRepoErr := repo.FetchContext(ctx, &git.FetchOptions{ RemoteName: "origin", Force: true, Auth: opts.Auth.Convert(), }) - if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) { - return nil, UpdateFailedError{ - Repository: opts.Repository, - Inner: err, + if fetchRepoErr != nil && !errors.Is(fetchRepoErr, git.NoErrAlreadyUpToDate) { + workTree, err := repo.Worktree() + if err != nil { + return nil, err } + return &GitRepo{ + opts: opts, + repo: repo, + workTree: workTree, + }, UpdateFailedError{ + Repository: opts.Repository, + Inner: fetchRepoErr, + } } // Finally, hard reset to our requested revision. @@ -92,7 +100,7 @@ func NewGitRepo(ctx context.Context, storagePath string, opts GitRepoOptions) (* opts: opts, repo: repo, workTree: workTree, - }, nil + }, err } func isRepoCloned(dir string) bool { @@ -103,15 +111,16 @@ func isRepoCloned(dir string) bool { // Update updates the repository by fetching new content and re-checking out to // latest version of Revision. func (repo *GitRepo) Update(ctx context.Context) error { - err := repo.repo.FetchContext(ctx, &git.FetchOptions{ + var err error + fetchRepoErr := repo.repo.FetchContext(ctx, &git.FetchOptions{ RemoteName: "origin", Force: true, Auth: repo.opts.Auth.Convert(), }) - if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) { + if fetchRepoErr != nil && !errors.Is(fetchRepoErr, git.NoErrAlreadyUpToDate) { return UpdateFailedError{ Repository: repo.opts.Repository, - Inner: err, + Inner: fetchRepoErr, } } @@ -120,7 +129,6 @@ func (repo *GitRepo) Update(ctx context.Context) error { if err != nil { return InvalidRevisionError{Revision: repo.opts.Revision} } - err = repo.workTree.Reset(&git.ResetOptions{ Commit: hash, Mode: git.HardReset,