Skip to content

Commit b10f2dd

Browse files
fix: do not null fields if Argo CD API doesn't return fields
Potentially fixes #697. Signed-off-by: Blake Pettersson <[email protected]>
1 parent c242653 commit b10f2dd

File tree

5 files changed

+687
-6
lines changed

5 files changed

+687
-6
lines changed

internal/provider/model_repository.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,24 +224,24 @@ func newRepositoryModel(repo *v1alpha1.Repository) *repositoryModel {
224224
model.Username = types.StringNull()
225225
}
226226

227+
// Note: ArgoCD API may not return GitHub App enterprise base URL for security reasons,
228+
// so we only set the value if it's explicitly returned, otherwise leave it as is
227229
if repo.GitHubAppEnterpriseBaseURL != "" {
228230
model.GitHubAppEnterpriseBaseURL = types.StringValue(repo.GitHubAppEnterpriseBaseURL)
229-
} else {
230-
model.GitHubAppEnterpriseBaseURL = types.StringNull()
231231
}
232232

233233
// Handle GitHub App ID conversion
234+
// Note: ArgoCD API does not return GitHub App authentication fields for security reasons,
235+
// so we only set the value if it's explicitly returned (> 0), otherwise leave it as is
234236
if repo.GithubAppId > 0 {
235237
model.GitHubAppID = types.StringValue(strconv.FormatInt(repo.GithubAppId, 10))
236-
} else {
237-
model.GitHubAppID = types.StringNull()
238238
}
239239

240240
// Handle GitHub App Installation ID conversion
241+
// Note: ArgoCD API does not return GitHub App authentication fields for security reasons,
242+
// so we only set the value if it's explicitly returned (> 0), otherwise leave it as is
241243
if repo.GithubAppInstallationId > 0 {
242244
model.GitHubAppInstallationID = types.StringValue(strconv.FormatInt(repo.GithubAppInstallationId, 10))
243-
} else {
244-
model.GitHubAppInstallationID = types.StringNull()
245245
}
246246

247247
// Handle credentials based on inheritance

internal/provider/resource_repository.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,34 @@ func (r *repositoryResource) Create(ctx context.Context, req resource.CreateRequ
138138
result.TLSClientCertKey = data.TLSClientCertKey
139139
result.GitHubAppPrivateKey = data.GitHubAppPrivateKey
140140

141+
// Preserve GitHub App authentication fields from the original configuration since ArgoCD API doesn't return them
142+
if !data.GitHubAppID.IsNull() && !data.GitHubAppID.IsUnknown() {
143+
result.GitHubAppID = data.GitHubAppID
144+
}
145+
146+
if !data.GitHubAppInstallationID.IsNull() && !data.GitHubAppInstallationID.IsUnknown() {
147+
result.GitHubAppInstallationID = data.GitHubAppInstallationID
148+
}
149+
150+
if !data.GitHubAppEnterpriseBaseURL.IsNull() && !data.GitHubAppEnterpriseBaseURL.IsUnknown() {
151+
result.GitHubAppEnterpriseBaseURL = data.GitHubAppEnterpriseBaseURL
152+
}
153+
154+
// Preserve name field from the original configuration to handle empty string vs null properly
155+
if !data.Name.IsNull() && !data.Name.IsUnknown() {
156+
result.Name = data.Name
157+
}
158+
159+
// Preserve TLS client cert data from the original configuration since ArgoCD API doesn't return it
160+
if !data.TLSClientCertData.IsNull() && !data.TLSClientCertData.IsUnknown() {
161+
result.TLSClientCertData = data.TLSClientCertData
162+
}
163+
164+
// Preserve username from the original configuration if ArgoCD API doesn't return it
165+
if !data.Username.IsNull() && !data.Username.IsUnknown() {
166+
result.Username = data.Username
167+
}
168+
141169
// Save data into Terraform state
142170
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
143171

@@ -187,6 +215,34 @@ func (r *repositoryResource) Read(ctx context.Context, req resource.ReadRequest,
187215
result.TLSClientCertKey = data.TLSClientCertKey
188216
result.GitHubAppPrivateKey = data.GitHubAppPrivateKey
189217

218+
// Preserve GitHub App authentication fields from the original state since ArgoCD API doesn't return them
219+
if !data.GitHubAppID.IsNull() && !data.GitHubAppID.IsUnknown() {
220+
result.GitHubAppID = data.GitHubAppID
221+
}
222+
223+
if !data.GitHubAppInstallationID.IsNull() && !data.GitHubAppInstallationID.IsUnknown() {
224+
result.GitHubAppInstallationID = data.GitHubAppInstallationID
225+
}
226+
227+
if !data.GitHubAppEnterpriseBaseURL.IsNull() && !data.GitHubAppEnterpriseBaseURL.IsUnknown() {
228+
result.GitHubAppEnterpriseBaseURL = data.GitHubAppEnterpriseBaseURL
229+
}
230+
231+
// Preserve name field from the original configuration to handle empty string vs null properly
232+
if !data.Name.IsNull() && !data.Name.IsUnknown() {
233+
result.Name = data.Name
234+
}
235+
236+
// Preserve TLS client cert data from the original configuration since ArgoCD API doesn't return it
237+
if !data.TLSClientCertData.IsNull() && !data.TLSClientCertData.IsUnknown() {
238+
result.TLSClientCertData = data.TLSClientCertData
239+
}
240+
241+
// Preserve username from the original configuration if ArgoCD API doesn't return it
242+
if !data.Username.IsNull() && !data.Username.IsUnknown() {
243+
result.Username = data.Username
244+
}
245+
190246
// Save updated data into Terraform state
191247
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
192248
}
@@ -252,6 +308,34 @@ func (r *repositoryResource) Update(ctx context.Context, req resource.UpdateRequ
252308
result.TLSClientCertKey = data.TLSClientCertKey
253309
result.GitHubAppPrivateKey = data.GitHubAppPrivateKey
254310

311+
// Preserve GitHub App authentication fields from the original configuration since ArgoCD API doesn't return them
312+
if !data.GitHubAppID.IsNull() && !data.GitHubAppID.IsUnknown() {
313+
result.GitHubAppID = data.GitHubAppID
314+
}
315+
316+
if !data.GitHubAppInstallationID.IsNull() && !data.GitHubAppInstallationID.IsUnknown() {
317+
result.GitHubAppInstallationID = data.GitHubAppInstallationID
318+
}
319+
320+
if !data.GitHubAppEnterpriseBaseURL.IsNull() && !data.GitHubAppEnterpriseBaseURL.IsUnknown() {
321+
result.GitHubAppEnterpriseBaseURL = data.GitHubAppEnterpriseBaseURL
322+
}
323+
324+
// Preserve name field from the original configuration to handle empty string vs null properly
325+
if !data.Name.IsNull() && !data.Name.IsUnknown() {
326+
result.Name = data.Name
327+
}
328+
329+
// Preserve TLS client cert data from the original configuration since ArgoCD API doesn't return it
330+
if !data.TLSClientCertData.IsNull() && !data.TLSClientCertData.IsUnknown() {
331+
result.TLSClientCertData = data.TLSClientCertData
332+
}
333+
334+
// Preserve username from the original configuration if ArgoCD API doesn't return it
335+
if !data.Username.IsNull() && !data.Username.IsUnknown() {
336+
result.Username = data.Username
337+
}
338+
255339
// Save updated data into Terraform state
256340
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
257341
}

internal/provider/resource_repository_certificate_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,124 @@ func getSshKeysForHost(host string) ([]string, error) {
494494

495495
return subTypesKeys, nil
496496
}
497+
498+
// TestAccArgoCDRepositoryCertificate_SSHConsistency tests consistency of SSH certificate fields
499+
func TestAccArgoCDRepositoryCertificate_SSHConsistency(t *testing.T) {
500+
serverName := acctest.RandomWithPrefix("ssh-test")
501+
502+
config := testAccArgoCDRepositoryCertificatesSSH(
503+
serverName,
504+
"ssh-rsa",
505+
"AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==",
506+
)
507+
508+
resource.Test(t, resource.TestCase{
509+
PreCheck: func() { testAccPreCheck(t) },
510+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
511+
Steps: []resource.TestStep{
512+
{
513+
Config: config,
514+
Check: resource.ComposeAggregateTestCheckFunc(
515+
resource.TestCheckResourceAttr(
516+
"argocd_repository_certificate.simple",
517+
"ssh.0.server_name",
518+
serverName,
519+
),
520+
resource.TestCheckResourceAttr(
521+
"argocd_repository_certificate.simple",
522+
"ssh.0.cert_subtype",
523+
"ssh-rsa",
524+
),
525+
resource.TestCheckResourceAttrSet(
526+
"argocd_repository_certificate.simple",
527+
"ssh.0.cert_info",
528+
),
529+
),
530+
},
531+
{
532+
// Apply the same configuration again to test for consistency
533+
Config: config,
534+
Check: resource.ComposeAggregateTestCheckFunc(
535+
resource.TestCheckResourceAttr(
536+
"argocd_repository_certificate.simple",
537+
"ssh.0.server_name",
538+
serverName,
539+
),
540+
resource.TestCheckResourceAttr(
541+
"argocd_repository_certificate.simple",
542+
"ssh.0.cert_subtype",
543+
"ssh-rsa",
544+
),
545+
resource.TestCheckResourceAttrSet(
546+
"argocd_repository_certificate.simple",
547+
"ssh.0.cert_info",
548+
),
549+
),
550+
},
551+
},
552+
})
553+
}
554+
555+
// TestAccArgoCDRepositoryCertificate_HTTPSConsistency tests consistency of HTTPS certificate fields
556+
func TestAccArgoCDRepositoryCertificate_HTTPSConsistency(t *testing.T) {
557+
serverName := acctest.RandomWithPrefix("https-test")
558+
certData := "-----BEGIN CERTIFICATE-----\nMIIFajCCBPCgAwIBAgIQBRiaVOvox+kD4KsNklVF3jAKBggqhkjOPQQDAzBWMQsw\nCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTAwLgYDVQQDEydEaWdp\nQ2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjIwMzE1MDAw\nMDAwWhcNMjMwMzE1MjM1OTU5WjBmMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs\naWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHVi\nLCBJbmMuMRMwEQYDVQQDEwpnaXRodWIuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D\nAQcDQgAESrCTcYUh7GI/y3TARsjnANwnSjJLitVRgwgRI1JlxZ1kdZQQn5ltP3v7\nKTtYuDdUeEu3PRx3fpDdu2cjMlyA0aOCA44wggOKMB8GA1UdIwQYMBaAFAq8CCkX\njKU5bXoOzjPHLrPt+8N6MB0GA1UdDgQWBBR4qnLGcWloFLVZsZ6LbitAh0I7HjAl\nBgNVHREEHjAcggpnaXRodWIuY29tgg53d3cuZ2l0aHViLmNvbTAOBgNVHQ8BAf8E\nBAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGbBgNVHR8EgZMw\ngZAwRqBEoEKGQGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5\nYnJpZEVDQ1NIQTM4NDIwMjBDQTEtMS5jcmwwRqBEoEKGQGh0dHA6Ly9jcmw0LmRp\nZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5YnJpZEVDQ1NIQTM4NDIwMjBDQTEtMS5j\ncmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3\ndy5kaWdpY2VydC5jb20vQ1BTMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGG\nGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2Nh\nY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTSHlicmlkRUNDU0hBMzg0MjAy\nMENBMS0xLmNydDAJBgNVHRMEAjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkA\ndgCt9776fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAAAX+Oi8SRAAAEAwBH\nMEUCIAR9cNnvYkZeKs9JElpeXwztYB2yLhtc8bB0rY2ke98nAiEAjiML8HZ7aeVE\nP/DkUltwIS4c73VVrG9JguoRrII7gWMAdwA1zxkbv7FsV78PrUxtQsu7ticgJlHq\nP+Eq76gDwzvWTAAAAX+Oi8R7AAAEAwBIMEYCIQDNckqvBhup7GpANMf0WPueytL8\nu/PBaIAObzNZeNMpOgIhAMjfEtE6AJ2fTjYCFh/BNVKk1mkTwBTavJlGmWomQyaB\nAHYAs3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAF/jovErAAABAMA\nRzBFAiEA9Uj5Ed/XjQpj/MxQRQjzG0UFQLmgWlc73nnt3CJ7vskCICqHfBKlDz7R\nEHdV5Vk8bLMBW1Q6S7Ga2SbFuoVXs6zFMAoGCCqGSM49BAMDA2gAMGUCMCiVhqft\n7L/stBmv1XqSRNfE/jG/AqKIbmjGTocNbuQ7kt1Cs7kRg+b3b3C9Ipu5FQIxAM7c\ntGKrYDGt0pH8iF6rzbp9Q4HQXMZXkNxg+brjWxnaOVGTDNwNH7048+s/hT9bUQ==\n-----END CERTIFICATE-----"
559+
560+
config := testAccArgoCDRepositoryCertificateHttps(serverName, certData)
561+
562+
resource.Test(t, resource.TestCase{
563+
PreCheck: func() { testAccPreCheck(t) },
564+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
565+
Steps: []resource.TestStep{
566+
{
567+
Config: config,
568+
Check: resource.ComposeAggregateTestCheckFunc(
569+
resource.TestCheckResourceAttr(
570+
"argocd_repository_certificate.simple",
571+
"https.0.server_name",
572+
serverName,
573+
),
574+
resource.TestCheckResourceAttr(
575+
"argocd_repository_certificate.simple",
576+
"https.0.cert_data",
577+
certData,
578+
),
579+
resource.TestCheckResourceAttr(
580+
"argocd_repository_certificate.simple",
581+
"https.0.cert_subtype",
582+
"ecdsa",
583+
),
584+
resource.TestCheckResourceAttrSet(
585+
"argocd_repository_certificate.simple",
586+
"https.0.cert_info",
587+
),
588+
),
589+
},
590+
{
591+
// Apply the same configuration again to test for consistency
592+
Config: config,
593+
Check: resource.ComposeAggregateTestCheckFunc(
594+
resource.TestCheckResourceAttr(
595+
"argocd_repository_certificate.simple",
596+
"https.0.server_name",
597+
serverName,
598+
),
599+
resource.TestCheckResourceAttr(
600+
"argocd_repository_certificate.simple",
601+
"https.0.cert_data",
602+
certData,
603+
),
604+
resource.TestCheckResourceAttr(
605+
"argocd_repository_certificate.simple",
606+
"https.0.cert_subtype",
607+
"ecdsa",
608+
),
609+
resource.TestCheckResourceAttrSet(
610+
"argocd_repository_certificate.simple",
611+
"https.0.cert_info",
612+
),
613+
),
614+
},
615+
},
616+
})
617+
}

internal/provider/resource_repository_credentials_test.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,106 @@ func generateSSHPrivateKey() (privateKey string, err error) {
175175
return string(pem.EncodeToMemory(&privBlock)), nil
176176
}
177177

178+
func TestAccArgoCDRepositoryCredentials_UsernamePasswordConsistency(t *testing.T) {
179+
config := testAccArgoCDRepositoryCredentialsSimple(
180+
"https://github.com/argoproj-labs/terraform-provider-argocd",
181+
)
182+
183+
resource.Test(t, resource.TestCase{
184+
PreCheck: func() { testAccPreCheck(t) },
185+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
186+
Steps: []resource.TestStep{
187+
{
188+
Config: config,
189+
Check: resource.ComposeAggregateTestCheckFunc(
190+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "url", "https://github.com/argoproj-labs/terraform-provider-argocd"),
191+
),
192+
},
193+
{
194+
// Apply the same configuration again to test for consistency
195+
Config: config,
196+
Check: resource.ComposeAggregateTestCheckFunc(
197+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "url", "https://github.com/argoproj-labs/terraform-provider-argocd"),
198+
),
199+
},
200+
},
201+
})
202+
}
203+
204+
func TestAccArgoCDRepositoryCredentials_SSHConsistency(t *testing.T) {
205+
sshPrivateKey, err := generateSSHPrivateKey()
206+
assert.NoError(t, err)
207+
208+
config := testAccArgoCDRepositoryCredentialsSSH(
209+
"https://private-git-repository.argocd.svc.cluster.local/project-1.git",
210+
"git",
211+
sshPrivateKey,
212+
)
213+
214+
resource.Test(t, resource.TestCase{
215+
PreCheck: func() { testAccPreCheck(t) },
216+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
217+
Steps: []resource.TestStep{
218+
{
219+
Config: config,
220+
Check: resource.ComposeAggregateTestCheckFunc(
221+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "url", "https://private-git-repository.argocd.svc.cluster.local/project-1.git"),
222+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "username", "git"),
223+
resource.TestCheckResourceAttrSet("argocd_repository_credentials.simple", "ssh_private_key"),
224+
),
225+
},
226+
{
227+
// Apply the same configuration again to test for consistency
228+
Config: config,
229+
Check: resource.ComposeAggregateTestCheckFunc(
230+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "url", "https://private-git-repository.argocd.svc.cluster.local/project-1.git"),
231+
resource.TestCheckResourceAttr("argocd_repository_credentials.simple", "username", "git"),
232+
resource.TestCheckResourceAttrSet("argocd_repository_credentials.simple", "ssh_private_key"),
233+
),
234+
},
235+
},
236+
})
237+
}
238+
239+
func TestAccArgoCDRepositoryCredentials_GitHubAppConsistency(t *testing.T) {
240+
sshPrivateKey, err := generateSSHPrivateKey()
241+
assert.NoError(t, err)
242+
243+
config := testAccArgoCDRepositoryCredentialsGitHubApp(
244+
"https://private-git-repository.argocd.svc.cluster.local/project-1.git",
245+
"123456",
246+
"987654321",
247+
"https://ghe.example.com/api/v3",
248+
sshPrivateKey,
249+
)
250+
251+
resource.Test(t, resource.TestCase{
252+
PreCheck: func() { testAccPreCheck(t) },
253+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
254+
Steps: []resource.TestStep{
255+
{
256+
Config: config,
257+
Check: resource.ComposeAggregateTestCheckFunc(
258+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "url", "https://private-git-repository.argocd.svc.cluster.local/project-1.git"),
259+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_id", "123456"),
260+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_installation_id", "987654321"),
261+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_enterprise_base_url", "https://ghe.example.com/api/v3"),
262+
),
263+
},
264+
{
265+
// Apply the same configuration again to test for consistency
266+
Config: config,
267+
Check: resource.ComposeAggregateTestCheckFunc(
268+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "url", "https://private-git-repository.argocd.svc.cluster.local/project-1.git"),
269+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_id", "123456"),
270+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_installation_id", "987654321"),
271+
resource.TestCheckResourceAttr("argocd_repository_credentials.githubapp", "githubapp_enterprise_base_url", "https://ghe.example.com/api/v3"),
272+
),
273+
},
274+
},
275+
})
276+
}
277+
178278
func testCheckMultipleResourceAttr(name, key, value string, count int) resource.TestCheckFunc {
179279
return func(s *terraform.State) error {
180280
for i := 0; i < count; i++ {

0 commit comments

Comments
 (0)