Skip to content

Commit 647debc

Browse files
authored
add: project scope support for clusters (#143)
* tests: add precheck function for feature support Useful for acceptance tests that should only be run when specific features are supported by ArgoCD version. If a feature is not supported the test is skipped, not failed. For local testing, you now have to add the `ARGOCD_VERSION` environment variable until a better long-term solution is found. * add: project scope support for clusters
1 parent 618ceb4 commit 647debc

File tree

8 files changed

+126
-0
lines changed

8 files changed

+126
-0
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ jobs:
4949
netstat -tulpn
5050
5151
- name: Run acceptance tests
52+
env:
53+
ARGOCD_VERSION: ${{ matrix.argocd_version }}
5254
run: sh scripts/testacc.sh

argocd/features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
featureIgnoreDiffJQPathExpressions
2424
featureRepositoryGet
2525
featureTokenIDs
26+
featureProjectScopedClusters
2627
)
2728

2829
var (
@@ -31,6 +32,7 @@ var (
3132
featureIgnoreDiffJQPathExpressions: semver.MustParse("2.1.0"),
3233
featureRepositoryGet: semver.MustParse("1.6.0"),
3334
featureTokenIDs: semver.MustParse("1.5.3"),
35+
featureProjectScopedClusters: semver.MustParse("2.2.0"),
3436
}
3537
)
3638

argocd/provider_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"os"
55
"testing"
66

7+
"github.com/Masterminds/semver"
78
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
89
)
910

@@ -41,3 +42,21 @@ func testAccPreCheck(t *testing.T) {
4142
t.Fatal("ARGOCD_INSECURE should be set for acceptance tests")
4243
}
4344
}
45+
46+
func testAccPreCheckFeatureSupported(t *testing.T, feature int) {
47+
v := os.Getenv("ARGOCD_VERSION")
48+
if v == "" {
49+
t.Skip("ARGOCD_VERSION must be set set for feature supported acceptance tests")
50+
}
51+
serverVersion, err := semver.NewVersion(v)
52+
if err != nil {
53+
t.Fatalf("could not parse ARGOCD_VERSION as semantic version: %s", v)
54+
}
55+
versionConstraint, ok := featureVersionConstraintsMap[feature]
56+
if !ok {
57+
t.Fatal("feature constraint is not handled by the provider")
58+
}
59+
if i := versionConstraint.Compare(serverVersion); i == 1 {
60+
t.Skipf("version %s does not support feature", v)
61+
}
62+
}

argocd/resource_argocd_cluster.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ func resourceArgoCDClusterCreate(ctx context.Context, d *schema.ResourceData, me
4747
}
4848

4949
}
50+
51+
featureProjectScopedClustersSupported, err := server.isFeatureSupported(featureProjectScopedClusters)
52+
if err != nil {
53+
return []diag.Diagnostic{
54+
{
55+
Severity: diag.Error,
56+
Summary: "feature not supported",
57+
Detail: err.Error(),
58+
},
59+
}
60+
}
61+
if !featureProjectScopedClustersSupported && cluster.Project != "" {
62+
return []diag.Diagnostic{
63+
{
64+
Severity: diag.Error,
65+
Summary: fmt.Sprintf(
66+
"cluster project is only supported from ArgoCD %s onwards",
67+
featureVersionConstraintsMap[featureProjectScopedClusters].String()),
68+
Detail: "See https://argo-cd.readthedocs.io/en/stable/user-guide/projects/#project-scoped-repositories-and-clusters",
69+
},
70+
}
71+
}
72+
5073
c, err := client.Create(ctx, &clusterClient.ClusterCreateRequest{
5174
Cluster: cluster, Upsert: true})
5275
if err != nil {
@@ -128,6 +151,29 @@ func resourceArgoCDClusterUpdate(ctx context.Context, d *schema.ResourceData, me
128151
},
129152
}
130153
}
154+
155+
featureProjectScopedClustersSupported, err := server.isFeatureSupported(featureProjectScopedClusters)
156+
if err != nil {
157+
return []diag.Diagnostic{
158+
{
159+
Severity: diag.Error,
160+
Summary: "feature not supported",
161+
Detail: err.Error(),
162+
},
163+
}
164+
}
165+
if !featureProjectScopedClustersSupported && cluster.Project != "" {
166+
return []diag.Diagnostic{
167+
{
168+
Severity: diag.Error,
169+
Summary: fmt.Sprintf(
170+
"cluster project is only supported from ArgoCD %s onwards",
171+
featureVersionConstraintsMap[featureProjectScopedClusters].String()),
172+
Detail: "See https://argo-cd.readthedocs.io/en/stable/user-guide/projects/#project-scoped-repositories-and-clusters",
173+
},
174+
}
175+
}
176+
131177
_, err = client.Update(ctx, &clusterClient.ClusterUpdateRequest{Cluster: cluster})
132178
if err != nil {
133179
if strings.Contains(err.Error(), "NotFound") {

argocd/resource_argocd_cluster_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,35 @@ func TestAccArgoCDCluster(t *testing.T) {
7171
})
7272
}
7373

74+
func TestAccArgoCDCluster_projectScope(t *testing.T) {
75+
resource.Test(t, resource.TestCase{
76+
PreCheck: func() { testAccPreCheck(t); testAccPreCheckFeatureSupported(t, featureProjectScopedClusters) },
77+
ProviderFactories: testAccProviders,
78+
Steps: []resource.TestStep{
79+
{
80+
Config: testAccArgoCDClusterProjectScope(acctest.RandString(10), "myproject1"),
81+
Check: resource.ComposeTestCheckFunc(
82+
resource.TestCheckResourceAttr(
83+
"argocd_cluster.project_scope",
84+
"info.0.connection_state.0.status",
85+
"Successful",
86+
),
87+
resource.TestCheckResourceAttr(
88+
"argocd_cluster.project_scope",
89+
"config.0.tls_client_config.0.insecure",
90+
"true",
91+
),
92+
resource.TestCheckResourceAttr(
93+
"argocd_cluster.project_scope",
94+
"project",
95+
"myproject1",
96+
),
97+
),
98+
},
99+
},
100+
})
101+
}
102+
74103
func testAccArgoCDClusterBearerToken(clusterName string) string {
75104
return fmt.Sprintf(`
76105
resource "argocd_cluster" "simple" {
@@ -118,6 +147,23 @@ EOT
118147
`, clusterName, rc.KeyData, rc.CertData, rc.CAData, rc.ServerName)
119148
}
120149

150+
func testAccArgoCDClusterProjectScope(clusterName, projectName string) string {
151+
return fmt.Sprintf(`
152+
resource "argocd_cluster" "project_scope" {
153+
server = "https://kubernetes.default.svc.cluster.local"
154+
name = "%s"
155+
project = "%s"
156+
config {
157+
# Uses Kind's bootstrap token whose ttl is 24 hours after cluster bootstrap.
158+
bearer_token = "abcdef.0123456789abcdef"
159+
tls_client_config {
160+
insecure = true
161+
}
162+
}
163+
}
164+
`, clusterName, projectName)
165+
}
166+
121167
// getInternalRestConfig returns the internal Kubernetes cluster REST config.
122168
func getInternalRestConfig() (*rest.Config, error) {
123169
rc := &rest.Config{}

argocd/schema_cluster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,5 +205,10 @@ func clusterSchema() map[string]*schema.Schema {
205205
},
206206
},
207207
},
208+
"project": {
209+
Type: schema.TypeString,
210+
Description: "Add cluster scoped to project",
211+
Optional: true,
212+
},
208213
}
209214
}

argocd/structure_cluster.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ func expandCluster(d *schema.ResourceData) (*application.Cluster, error) {
3535
cluster.Annotations = m.Annotations
3636
cluster.Labels = m.Labels
3737

38+
if v, ok := d.GetOk("project"); ok {
39+
cluster.Project = v.(string)
40+
}
41+
3842
return cluster, err
3943
}
4044

@@ -113,6 +117,7 @@ func flattenCluster(cluster *application.Cluster, d *schema.ResourceData) error
113117
"namespaces": cluster.Namespaces,
114118
"info": flattenClusterInfo(cluster.Info),
115119
"config": flattenClusterConfig(cluster.Config, d),
120+
"project": cluster.Project,
116121
}
117122
if cluster.Shard != nil {
118123
r["shard"] = convertInt64PointerToString(cluster.Shard)

docs/resources/cluster.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ resource "argocd_cluster" "eks" {
121121
* `namespaces` - (Optional) Holds list of namespaces which are accessible in that cluster. Cluster level resources would be ignored if namespace list is not empty..
122122
* `config` - (Optional) The configuration specification, nested attributes are documented below.
123123
* `metadata` - (Optional) Cluster metadata, nested attributes are documented below.
124+
* `project` - (Optional) Scope cluster to ArgoCD project. If omitted, cluster will be global. Requires ArgoCD 2.2.0 onwards.
124125

125126
The `config` block can have the following attributes:
126127

0 commit comments

Comments
 (0)