Skip to content

Commit d5d059c

Browse files
committed
fix(argocd_cluster): use cluster list api to avoid 403 issues with cluster get api at cluster read time
Signed-off-by: Hugo HARS <[email protected]>
1 parent 76d2d70 commit d5d059c

File tree

2 files changed

+152
-1
lines changed

2 files changed

+152
-1
lines changed

argocd/resource_argocd_cluster.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func resourceArgoCDClusterCreate(ctx context.Context, d *schema.ResourceData, me
4343

4444
// Cluster are unique by "server address" so we should check there is no existing cluster with this address before
4545
existingClusters, err := si.ClusterClient.List(ctx, &clusterClient.ClusterQuery{
46+
// Starting argo-cd server v2.8.0 filtering on list api endpoint is fixed, else it is ignored, see:
47+
// - https://github.com/oboukili/terraform-provider-argocd/issues/266#issuecomment-1739122022
48+
// - https://github.com/argoproj/argo-cd/pull/13363
4649
Id: &clusterClient.ClusterID{
4750
Type: "server",
4851
Value: rtrimmedServer,
@@ -53,6 +56,7 @@ func resourceArgoCDClusterCreate(ctx context.Context, d *schema.ResourceData, me
5356
return errorToDiagnostics(fmt.Sprintf("failed to list existing clusters when creating cluster %s", cluster.Server), err)
5457
}
5558

59+
// Here we will filter ourselves on the list so that we are backward compatible for argo-cd server with version < v2.8.0 (see coment above)
5660
if len(existingClusters.Items) > 0 {
5761
for _, existingCluster := range existingClusters.Items {
5862
if rtrimmedServer == strings.TrimRight(existingCluster.Server, "/") {
@@ -103,7 +107,57 @@ func resourceArgoCDClusterRead(ctx context.Context, d *schema.ResourceData, meta
103107
return nil
104108
}
105109

106-
return argoCDAPIError("read", "cluster", d.Id(), err)
110+
// Fix for https://github.com/argoproj-labs/terraform-provider-argocd/issues/266
111+
// This fix is added here as a workaround to ensure backward compatibility, as
112+
// it is triggered only on the specific usecase where the issue happens.
113+
// Additional remarks about this code:
114+
// * it is a copy/paste of the code used by resourceArgoCDClusterCreate to check if
115+
// the cluster already exists (with some obvious changes to return value and mutex type)
116+
// * it should at term replace the `si.ClusterClient.Get` code for this method
117+
if strings.Contains(err.Error(), "PermissionDenied") {
118+
cluster, err := expandCluster(d)
119+
if err != nil {
120+
return errorToDiagnostics("failed to expand cluster", err)
121+
}
122+
123+
tokenMutexClusters.RLock()
124+
125+
rtrimmedServer := strings.TrimRight(cluster.Server, "/")
126+
127+
// Cluster are unique by "server address" so we should check there is no existing cluster with this address before
128+
existingClusters, err := si.ClusterClient.List(ctx, &clusterClient.ClusterQuery{
129+
// Starting argo-cd server v2.8.0 filtering on list api endpoint is fixed, else it is ignored, see:
130+
// - https://github.com/oboukili/terraform-provider-argocd/issues/266#issuecomment-1739122022
131+
// - https://github.com/argoproj/argo-cd/pull/13363
132+
Id: &clusterClient.ClusterID{
133+
Type: "server",
134+
Value: rtrimmedServer,
135+
},
136+
})
137+
138+
tokenMutexClusters.RUnlock()
139+
140+
if err != nil {
141+
return errorToDiagnostics(fmt.Sprintf("failed to list existing clusters when reading cluster %s", cluster.Server), err)
142+
}
143+
144+
// Here we will filter ourselves on the list so that we are backward compatible for argo-cd server with version < v2.8.0 (see coment above)
145+
if len(existingClusters.Items) > 0 {
146+
for _, existingCluster := range existingClusters.Items {
147+
if rtrimmedServer == strings.TrimRight(existingCluster.Server, "/") {
148+
// Cluster was found, return
149+
return nil
150+
}
151+
}
152+
}
153+
154+
// Cluster was not found, return with empty Id
155+
d.SetId("")
156+
157+
return nil
158+
} else {
159+
return argoCDAPIError("read", "cluster", d.Id(), err)
160+
}
107161
}
108162

109163
if err = flattenCluster(c, d); err != nil {

argocd/resource_argocd_cluster_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
package argocd
22

33
import (
4+
"context"
45
"fmt"
6+
"os"
57
"regexp"
68
"runtime"
9+
"strconv"
710
"testing"
11+
"time"
812

13+
"github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
14+
"github.com/hashicorp/terraform-plugin-framework/types"
915
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
1016
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
17+
"github.com/oboukili/terraform-provider-argocd/internal/provider"
1118
"k8s.io/client-go/rest"
1219
"k8s.io/client-go/tools/clientcmd"
1320
"k8s.io/client-go/util/homedir"
@@ -292,6 +299,74 @@ func TestAccArgoCDCluster_invalidSameServer(t *testing.T) {
292299
})
293300
}
294301

302+
func TestAccArgoCDCluster_outsideDeletion(t *testing.T) {
303+
clusterName := acctest.RandString(10)
304+
resource.Test(t, resource.TestCase{
305+
PreCheck: func() { testAccPreCheck(t) },
306+
ProviderFactories: testAccProviders,
307+
Steps: []resource.TestStep{
308+
{
309+
Config: testAccArgoCDClusterMetadata(clusterName),
310+
Check: resource.ComposeTestCheckFunc(
311+
resource.TestCheckResourceAttr(
312+
"argocd_cluster.cluster_metadata",
313+
"info.0.connection_state.0.status",
314+
"Successful",
315+
),
316+
resource.TestCheckResourceAttr(
317+
"argocd_cluster.cluster_metadata",
318+
"config.0.tls_client_config.0.insecure",
319+
"true",
320+
),
321+
resource.TestCheckResourceAttr(
322+
"argocd_cluster.cluster_metadata",
323+
"name",
324+
clusterName,
325+
),
326+
),
327+
},
328+
{
329+
PreConfig: func() {
330+
// delete cluster and validate referesh generates a plan
331+
// (non-regression test for https://github.com/oboukili/terraform-provider-argocd/issues/266)
332+
si, err := getServerInterface()
333+
if err != nil {
334+
t.Error(fmt.Errorf("failed to get server interface: %s", err.Error()))
335+
}
336+
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
337+
defer cancel()
338+
_, err = si.ClusterClient.Delete(ctx, &cluster.ClusterQuery{Name: clusterName})
339+
if err != nil {
340+
t.Error(fmt.Errorf("failed to delete cluster '%s': %s", clusterName, err.Error()))
341+
}
342+
},
343+
RefreshState: true,
344+
ExpectNonEmptyPlan: true,
345+
},
346+
{
347+
Config: testAccArgoCDClusterMetadata(clusterName),
348+
Check: resource.ComposeTestCheckFunc(
349+
resource.TestCheckResourceAttr(
350+
"argocd_cluster.cluster_metadata",
351+
"info.0.connection_state.0.status",
352+
"Successful",
353+
),
354+
resource.TestCheckResourceAttr(
355+
"argocd_cluster.cluster_metadata",
356+
"config.0.tls_client_config.0.insecure",
357+
"true",
358+
),
359+
resource.TestCheckResourceAttr(
360+
"argocd_cluster.cluster_metadata",
361+
"name",
362+
clusterName,
363+
),
364+
),
365+
},
366+
},
367+
})
368+
}
369+
295370
func TestAccArgoCDCluster_namespacesErrorWhenEmpty(t *testing.T) {
296371
name := acctest.RandString(10)
297372

@@ -619,3 +694,25 @@ func getInternalRestConfig() (*rest.Config, error) {
619694

620695
return nil, fmt.Errorf("could not find a kind-argocd cluster from the current ~/.kube/config file")
621696
}
697+
698+
// build & init ArgoCD server interface
699+
func getServerInterface() (*provider.ServerInterface, error) {
700+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
701+
defer cancel()
702+
insecure, err := strconv.ParseBool(os.Getenv("ARGOCD_INSECURE"))
703+
if err != nil {
704+
return nil, fmt.Errorf("failed to parse 'ARGOCD_INSECURE' env var to bool: %s", err.Error())
705+
}
706+
si := provider.NewServerInterface(provider.ArgoCDProviderConfig{
707+
ServerAddr: types.StringValue(os.Getenv("ARGOCD_SERVER")),
708+
Insecure: types.BoolValue(insecure),
709+
Username: types.StringValue(os.Getenv("ARGOCD_AUTH_USERNAME")),
710+
Password: types.StringValue(os.Getenv("ARGOCD_AUTH_PASSWORD")),
711+
})
712+
diag := si.InitClients(ctx)
713+
if diag.HasError() {
714+
return nil, fmt.Errorf("failed to init clients: %v", diag.Errors())
715+
}
716+
717+
return si, nil
718+
}

0 commit comments

Comments
 (0)