Skip to content

Commit 50ff934

Browse files
authored
fix: inconsistency in saving empty GitOps repository (#6612)
* fix: gitops provider multiple save error * updated const ArgoRepoSyncDelayErr as per new ArgoCd update * fix: unable resolve 'HEAD' to a commit SHA * fix: git clone issue for empty repository * fix: ambiguous argument 'origin/HEAD': unknown revision or path not in the working tree. * fix: SSH known host confirmation issue on cloning * chore: updated default value of ARGO_REPO_REGISTER_RETRY_COUNT * chore: incorporated review comments * chore: removed dead code * chore: minor refactorings * chore: added integration test cases * fix: added handling for argocd older versions
1 parent f4d311e commit 50ff934

27 files changed

+661
-251
lines changed

client/argocdServer/ArgoClientWrapperService.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ import (
5151
)
5252

5353
type ACDConfig struct {
54-
ArgoCDAutoSyncEnabled bool `env:"ARGO_AUTO_SYNC_ENABLED" envDefault:"true" description:"If enabled all argocd application will have auto sync enabled"` // will gradually switch this flag to false in enterprise
55-
RegisterRepoMaxRetryCount int `env:"ARGO_REPO_REGISTER_RETRY_COUNT" envDefault:"3" description:"Argo app registration in argo retries on deployment"`
56-
RegisterRepoMaxRetryDelay int `env:"ARGO_REPO_REGISTER_RETRY_DELAY" envDefault:"10" description:"Argo app registration in argo cd on deployment delay between retry"`
54+
ArgoCDAutoSyncEnabled bool `env:"ARGO_AUTO_SYNC_ENABLED" envDefault:"true" description:"If enabled all argocd application will have auto sync enabled" example:"true" deprecated:"false"` // will gradually switch this flag to false in enterprise
55+
RegisterRepoMaxRetryCount int `env:"ARGO_REPO_REGISTER_RETRY_COUNT" envDefault:"4" description:"Retry count for registering a GitOps repository to ArgoCD" example:"3" deprecated:"false"`
56+
RegisterRepoMaxRetryDelay int `env:"ARGO_REPO_REGISTER_RETRY_DELAY" envDefault:"5" description:"Delay (in Seconds) between the retries for registering a GitOps repository to ArgoCD" example:"5" deprecated:"false"`
5757
}
5858

5959
func (config *ACDConfig) IsManualSyncEnabled() bool {
@@ -105,7 +105,7 @@ type ApplicationClientWrapper interface {
105105
// IsArgoAppPatchRequired decides weather the v1alpha1.ApplicationSource requires to be updated
106106
IsArgoAppPatchRequired(argoAppSpec *v1alpha1.ApplicationSource, currentGitRepoUrl, currentTargetRevision, currentChartPath string) bool
107107

108-
// GetGitOpsRepoName returns the GitOps repository name, configured for the argoCd app
108+
// GetGitOpsRepoNameForApplication returns the GitOps repository name, configured for the argoCd app
109109
GetGitOpsRepoNameForApplication(ctx context.Context, appName string) (gitOpsRepoName string, err error)
110110

111111
GetGitOpsRepoURLForApplication(ctx context.Context, appName string) (gitOpsRepoURL string, err error)
@@ -127,6 +127,7 @@ type RepoCredsClientWrapper interface {
127127
type CertificateClientWrapper interface {
128128
CreateCertificate(ctx context.Context, query *certificate.RepositoryCertificateCreateRequest) (*v1alpha1.RepositoryCertificateList, error)
129129
DeleteCertificate(ctx context.Context, query *certificate.RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error)
130+
CertificateClientWrapperEnt
130131
}
131132

132133
type ClusterClientWrapper interface {
@@ -147,7 +148,7 @@ type ArgoClientWrapperServiceImpl struct {
147148
repositoryService repository.ServiceClient
148149
clusterClient cluster.ServiceClient
149150
repoCredsClient repocreds2.ServiceClient
150-
CertificateClient certificate2.ServiceClient
151+
certificateClient certificate2.ServiceClient
151152
logger *zap.SugaredLogger
152153
ACDConfig *ACDConfig
153154
gitOpsConfigReadService config.GitOpsConfigReadService
@@ -176,7 +177,7 @@ func NewArgoClientWrapperServiceImpl(
176177
repositoryService: repositoryService,
177178
clusterClient: clusterClient,
178179
repoCredsClient: repocredsClient,
179-
CertificateClient: CertificateClient,
180+
certificateClient: CertificateClient,
180181
logger: logger,
181182
ACDConfig: ACDConfig,
182183
gitOpsConfigReadService: gitOpsConfigReadService,
@@ -521,7 +522,7 @@ func (impl *ArgoClientWrapperServiceImpl) createRepoInArgoCd(ctx context.Context
521522
}
522523
repo, err := impl.repositoryService.Create(ctx, grpcConfig, &repository2.RepoCreateRequest{Repo: repo, Upsert: true})
523524
if err != nil {
524-
impl.logger.Errorw("error in creating argo Repository", "err", err)
525+
impl.logger.Errorw("error in creating argo Repository", "url", gitOpsRepoUrl, "err", err)
525526
return err
526527
}
527528
return nil
@@ -530,7 +531,8 @@ func (impl *ArgoClientWrapperServiceImpl) createRepoInArgoCd(ctx context.Context
530531
// isRetryableArgoRepoCreationError returns whether to retry or not, based on the error returned from callback func
531532
// In createRepoInArgoCd, we will retry only if the error matches to bean.ArgoRepoSyncDelayErr
532533
func (impl *ArgoClientWrapperServiceImpl) isRetryableArgoRepoCreationError(argoCdErr error) bool {
533-
return strings.Contains(argoCdErr.Error(), bean.ArgoRepoSyncDelayErr)
534+
return strings.Contains(argoCdErr.Error(), bean.ArgoRepoSyncDelayErr) ||
535+
strings.Contains(argoCdErr.Error(), bean.ArgoRepoSyncDelayErrOld)
534536
}
535537

536538
// handleArgoRepoCreationError - manages the error thrown while performing createRepoInArgoCd
@@ -543,15 +545,18 @@ func (impl *ArgoClientWrapperServiceImpl) handleArgoRepoCreationError(ctx contex
543545
}
544546
}
545547
if isEmptyRepoError {
546-
// - found empty repository, create some file in repository
548+
impl.logger.Infow("handling for empty repo", "url", gitOpsRepoUrl)
549+
// - found empty repository (there is no origin/HEAD)
550+
// - create new commit on HEAD (default branch) with a README file
551+
// - then register the repository in ArgoCD
547552
gitOpsRepoName := impl.gitOpsConfigReadService.GetGitOpsRepoNameFromUrl(gitOpsRepoUrl)
548-
err := impl.gitOperationService.CreateReadmeInGitRepo(ctx, gitOpsRepoName, targetRevision, userId)
553+
err := impl.gitOperationService.CreateFirstCommitOnHead(ctx, gitOpsRepoName, userId)
549554
if err != nil {
550555
impl.logger.Errorw("error in creating file in git repo", "err", err)
551556
return err
552557
}
553558
}
554-
// try to register with after creating readme file
559+
// try to register with after commiting a file to origin/HEAD
555560
return impl.createRepoInArgoCd(ctx, grpcConfig, gitOpsRepoUrl)
556561
}
557562

@@ -586,13 +591,13 @@ func (impl *ArgoClientWrapperServiceImpl) CreateCertificate(ctx context.Context,
586591
if err != nil {
587592
return nil, err
588593
}
589-
return impl.CertificateClient.CreateCertificate(ctx, grpcConfig, query)
594+
return impl.certificateClient.CreateCertificate(ctx, grpcConfig, query)
590595
}
591596

592597
func (impl *ArgoClientWrapperServiceImpl) DeleteCertificate(ctx context.Context, query *certificate.RepositoryCertificateQuery, opts ...grpc.CallOption) (*v1alpha1.RepositoryCertificateList, error) {
593598
grpcConfig, err := impl.acdConfigGetter.GetGRPCConfig()
594599
if err != nil {
595600
return nil, err
596601
}
597-
return impl.CertificateClient.DeleteCertificate(ctx, grpcConfig, query, opts...)
602+
return impl.certificateClient.DeleteCertificate(ctx, grpcConfig, query, opts...)
598603
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright (c) 2024. Devtron Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package argocdServer
18+
19+
type CertificateClientWrapperEnt interface {
20+
}

client/argocdServer/bean/bean.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ const RegisterRepoMaxRetryCount = 3
4646
var EmptyRepoErrorList = []string{"failed to get index: 404 Not Found", "remote repository is empty"}
4747

4848
// ArgoRepoSyncDelayErr - This error occurs inconsistently; ArgoCD requires 80-120s after last commit for create repository operation
49-
const ArgoRepoSyncDelayErr = "Unable to resolve 'HEAD' to a commit SHA"
49+
// Error message reference: https://github.com/argoproj/argo-cd/blob/master/util/git/client.go#L718
50+
const ArgoRepoSyncDelayErr = "unable to resolve 'HEAD' to a commit SHA" // argocd version <= v2.13.0
51+
const ArgoRepoSyncDelayErrOld = "Unable to resolve 'HEAD' to a commit SHA" // argocd version > v2.13.0
5052

5153
const (
5254
Degraded = "Degraded"

client/argocdServer/k8sClient.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ func (impl ArgoK8sClientImpl) DeleteArgoApplication(ctx context.Context, k8sConf
227227
patchType := types.MergePatchType
228228
patchJSON := ""
229229

230-
//TODO: ayush test cascade delete
231230
if cascadeDelete {
232231
patchJSON = `{"metadata": {"finalizers": ["resources-finalizer.argocd.argoproj.io"]}}`
233232
} else {

env_gen.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

env_gen.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,11 @@
131131
| APP_SYNC_JOB_RESOURCES_OBJ | string | | To pass the resource of app sync | | false |
132132
| APP_SYNC_SERVICE_ACCOUNT | string |chart-sync | Service account to be used in app sync Job | | false |
133133
| APP_SYNC_SHUTDOWN_WAIT_DURATION | int |120 | | | false |
134-
| ARGO_AUTO_SYNC_ENABLED | bool |true | If enabled all argocd application will have auto sync enabled | | false |
134+
| ARGO_AUTO_SYNC_ENABLED | bool |true | If enabled all argocd application will have auto sync enabled | true | false |
135135
| ARGO_GIT_COMMIT_RETRY_COUNT_ON_CONFLICT | int |3 | retry argocd app manual sync if the timeline is stuck in ARGOCD_SYNC_INITIATED state for more than this defined time (in mins) | | false |
136136
| ARGO_GIT_COMMIT_RETRY_DELAY_ON_CONFLICT | int |1 | Delay on retrying the maifest commit the on gitops | | false |
137-
| ARGO_REPO_REGISTER_RETRY_COUNT | int |3 | Argo app registration in argo retries on deployment | | false |
138-
| ARGO_REPO_REGISTER_RETRY_DELAY | int |10 | Argo app registration in argo cd on deployment delay between retry | | false |
137+
| ARGO_REPO_REGISTER_RETRY_COUNT | int |4 | Retry count for registering a GitOps repository to ArgoCD | 3 | false |
138+
| ARGO_REPO_REGISTER_RETRY_DELAY | int |5 | Delay (in Seconds) between the retries for registering a GitOps repository to ArgoCD | 5 | false |
139139
| ASYNC_BUILDX_CACHE_EXPORT | bool |false | To enable async container image cache export | | false |
140140
| BATCH_SIZE | int |5 | there is feature to get URL's of services/ingresses. so to extract those, we need to parse all the servcie and ingress objects of the application. this BATCH_SIZE flag controls the no of these objects get parsed in one go. | | false |
141141
| BLOB_STORAGE_ENABLED | bool |false | | | false |
@@ -185,6 +185,9 @@
185185
| FEATURE_RESTART_WORKLOAD_BATCH_SIZE | int |1 | restart workload retrieval batch size | | false |
186186
| FEATURE_RESTART_WORKLOAD_WORKER_POOL_SIZE | int |5 | restart workload retrieval pool size | | false |
187187
| FORCE_SECURITY_SCANNING | bool |false | By enabling this no one can disable image scaning on ci-pipeline from UI | | false |
188+
| GITHUB_ORG_NAME | string | | | | false |
189+
| GITHUB_TOKEN | string | | | | false |
190+
| GITHUB_USERNAME | string | | | | false |
188191
| GITOPS_REPO_PREFIX | string | | Prefix for Gitops repo being creation for argocd application | | false |
189192
| GO_RUNTIME_ENV | string |production | | | false |
190193
| GRAFANA_HOST | string |localhost | Host URL for the grafana dashboard | | false |

internal/sql/repository/GitOpsConfigRepository.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ type GitOpsConfigRepository interface {
2929
GetAllGitOpsConfig() ([]*GitOpsConfig, error)
3030
GetAllGitOpsConfigCount() (int, error)
3131
GetGitOpsConfigByProvider(provider string) (*GitOpsConfig, error)
32+
CheckIfGitOpsProviderExist(provider string) (bool, error)
3233
GetGitOpsConfigActive() (*GitOpsConfig, error)
3334
GetConnection() *pg.DB
3435
GetEmailIdFromActiveGitOpsConfig() (string, error)
@@ -105,6 +106,13 @@ func (impl *GitOpsConfigRepositoryImpl) GetGitOpsConfigByProvider(provider strin
105106
return &model, err
106107
}
107108

109+
func (impl *GitOpsConfigRepositoryImpl) CheckIfGitOpsProviderExist(provider string) (bool, error) {
110+
found, err := impl.dbConnection.Model((*GitOpsConfig)(nil)).
111+
Where("provider = ?", provider).
112+
Exists()
113+
return found, err
114+
}
115+
108116
func (impl *GitOpsConfigRepositoryImpl) GetGitOpsConfigActive() (*GitOpsConfig, error) {
109117
var model GitOpsConfig
110118
err := impl.dbConnection.Model(&model).Where("active = ?", true).Limit(1).Select()

pkg/appStore/installedApp/service/FullMode/deployment/InstalledAppGitOpsService.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ import (
3232
"github.com/google/go-github/github"
3333
"github.com/microsoft/azure-devops-go-api/azuredevops"
3434
"github.com/xanzy/go-gitlab"
35-
"strconv"
36-
37-
//"github.com/xanzy/go-gitlab"
3835
"net/http"
36+
"strconv"
37+
"strings"
3938
)
4039

4140
type InstalledAppGitOpsService interface {
@@ -176,7 +175,7 @@ func (impl *FullModeDeploymentServiceImpl) parseGitRepoErrorResponse(err error)
176175
impl.Logger.Errorw("no content found while updating git repo gitlab, do auto fix", "error", err)
177176
noTargetFound = true
178177
}
179-
if err.Error() == git.BITBUCKET_REPO_NOT_FOUND_ERROR {
178+
if strings.Contains(err.Error(), git.BitbucketRepoNotFoundError.Error()) {
180179
impl.Logger.Errorw("no content found while updating git repo bitbucket, do auto fix", "error", err)
181180
noTargetFound = true
182181
}

pkg/deployment/gitOps/adapter/adapter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func GetGitOpsConfigBean(model *repository.GitOpsConfig) *apiGitOpsBean.GitOpsCo
3737
BitBucketWorkspaceId: model.BitBucketWorkspaceId,
3838
BitBucketProjectKey: model.BitBucketProjectKey,
3939
AllowCustomRepository: model.AllowCustomRepository,
40-
EnableTLSVerification: true,
40+
EnableTLSVerification: model.EnableTLSVerification,
4141
TLSConfig: &apiBean.TLSConfig{
4242
CaData: model.CaCert,
4343
TLSCertData: model.TlsCert,

pkg/deployment/gitOps/config/GitOpsConfigReadService.go

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ package config
1919
import (
2020
"errors"
2121
"fmt"
22-
bean3 "github.com/devtron-labs/devtron/api/bean"
2322
bean2 "github.com/devtron-labs/devtron/api/bean/gitOps"
2423
"github.com/devtron-labs/devtron/internal/constants"
2524
"github.com/devtron-labs/devtron/internal/sql/repository"
2625
internalUtil "github.com/devtron-labs/devtron/internal/util"
2726
"github.com/devtron-labs/devtron/pkg/auth/user"
2827
"github.com/devtron-labs/devtron/pkg/deployment/gitOps/adapter"
2928
"github.com/devtron-labs/devtron/pkg/deployment/gitOps/config/bean"
29+
gitAdapter "github.com/devtron-labs/devtron/pkg/deployment/gitOps/git/adapter"
30+
gitBean "github.com/devtron-labs/devtron/pkg/deployment/gitOps/git/bean"
3031
moduleBean "github.com/devtron-labs/devtron/pkg/module/bean"
3132
moduleRead "github.com/devtron-labs/devtron/pkg/module/read"
3233
moduleErr "github.com/devtron-labs/devtron/pkg/module/read/error"
@@ -49,6 +50,7 @@ type GitOpsConfigReadService interface {
4950
GetConfiguredGitOpsCount() (int, error)
5051
GetGitOpsProviderByRepoURL(gitRepoUrl string) (*bean2.GitOpsConfigDto, error)
5152
GetGitOpsById(id int) (*bean2.GitOpsConfigDto, error)
53+
GetGitConfig() (*gitBean.GitConfig, error)
5254
}
5355

5456
type GitOpsConfigReadServiceImpl struct {
@@ -211,25 +213,14 @@ func (impl *GitOpsConfigReadServiceImpl) GetGitOpsById(id int) (*bean2.GitOpsCon
211213
impl.logger.Errorw("error, GetGitOpsConfigById", "id", id, "err", err)
212214
return nil, err
213215
}
214-
config := &bean2.GitOpsConfigDto{
215-
Id: model.Id,
216-
Provider: model.Provider,
217-
GitHubOrgId: model.GitHubOrgId,
218-
GitLabGroupId: model.GitLabGroupId,
219-
Active: model.Active,
220-
Token: model.Token,
221-
Host: model.Host,
222-
Username: model.Username,
223-
UserId: model.CreatedBy,
224-
AzureProjectName: model.AzureProject,
225-
BitBucketWorkspaceId: model.BitBucketWorkspaceId,
226-
BitBucketProjectKey: model.BitBucketProjectKey,
227-
AllowCustomRepository: model.AllowCustomRepository,
228-
TLSConfig: &bean3.TLSConfig{
229-
CaData: model.CaCert,
230-
TLSCertData: model.TlsCert,
231-
TLSKeyData: model.TlsKey,
232-
},
233-
}
234-
return config, err
216+
return adapter.GetGitOpsConfigBean(model), err
217+
}
218+
219+
func (impl *GitOpsConfigReadServiceImpl) GetGitConfig() (*gitBean.GitConfig, error) {
220+
gitOpsConfig, err := impl.GetGitOpsConfigActive()
221+
if err != nil {
222+
impl.logger.Errorw("error while fetching gitops config", "err", err)
223+
return nil, err
224+
}
225+
return gitAdapter.ConvertGitOpsConfigToGitConfig(gitOpsConfig), err
235226
}

0 commit comments

Comments
 (0)