Skip to content

Commit

Permalink
add an argocd_cluster resource (#64)
Browse files Browse the repository at this point in the history
* fix: fix flaky semver features test
* feat: added argocd_cluster resource

Co-authored-by: Brian Fox <[email protected]>
  • Loading branch information
oboukili and onematchfox authored Feb 26, 2021
1 parent 07f9eac commit bc678d4
Show file tree
Hide file tree
Showing 15 changed files with 791 additions and 73 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,20 @@ provider "argocd" {
insecure = false # env ARGOCD_INSECURE
}
resource "argocd_cluster" "kubernetes" {
server = "https://1.2.3.4:12345"
name = "mycluster"
config {
bearer_token = "eyJhbGciOiJSUzI..."
tls_client_config {
ca_data = base64encode(file("path/to/ca.pem"))
// insecure = true
}
}
}
resource "argocd_repository_credentials" "private" {
url = "[email protected]"
username = "git"
Expand Down Expand Up @@ -178,7 +192,10 @@ resource "argocd_project" "myproject" {
sync_window {
kind = "deny"
applications = ["foo"]
clusters = ["in-cluster"]
clusters = [
"in-cluster",
argocd_cluster.cluster.name,
]
namespaces = ["default"]
duration = "12h"
schedule = "22 1 5 * *"
Expand Down
2 changes: 2 additions & 0 deletions argocd/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/Masterminds/semver"
"github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/application"
"github.com/argoproj/argo-cd/pkg/apiclient/cluster"
"github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
"github.com/argoproj/argo-cd/pkg/apiclient/repository"
Expand All @@ -28,6 +29,7 @@ var (
type ServerInterface struct {
ApiClient *apiclient.Client
ApplicationClient *application.ApplicationServiceClient
ClusterClient *cluster.ClusterServiceClient
ProjectClient *project.ProjectServiceClient
RepositoryClient *repository.RepositoryServiceClient
RepoCredsClient *repocreds.RepoCredsServiceClient
Expand Down
6 changes: 3 additions & 3 deletions argocd/features_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ func serverInterfaceTestData(t *testing.T, argocdVersion string, semverOperator
case semverLess:
v, err = semver.NewVersion(
fmt.Sprintf("%d.%d.%d",
mathutil.MinInt64(v.Major(), v.Major()-incMajor%v.Major()),
mathutil.MinInt64(v.Minor(), v.Minor()-incMinor%v.Minor()),
mathutil.MinInt64(v.Patch(), v.Patch()-incPatch%v.Patch()),
mathutil.MaxInt64(v.Major()-incMajor, 0),
mathutil.MaxInt64(v.Minor()-incMinor, 0),
mathutil.MaxInt64(v.Patch()-incPatch, 0),
))
assert.NoError(t, err)
default:
Expand Down
17 changes: 12 additions & 5 deletions argocd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/Masterminds/semver"
"github.com/argoproj/argo-cd/pkg/apiclient"
"github.com/argoproj/argo-cd/pkg/apiclient/application"
"github.com/argoproj/argo-cd/pkg/apiclient/cluster"
"github.com/argoproj/argo-cd/pkg/apiclient/project"
"github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
"github.com/argoproj/argo-cd/pkg/apiclient/repository"
Expand Down Expand Up @@ -110,6 +111,7 @@ func Provider() terraform.ResourceProvider {

ResourcesMap: map[string]*schema.Resource{
"argocd_application": resourceArgoCDApplication(),
"argocd_cluster": resourceArgoCDCluster(),
"argocd_project": resourceArgoCDProject(),
"argocd_project_token": resourceArgoCDProjectToken(),
"argocd_repository": resourceArgoCDRepository(),
Expand All @@ -120,16 +122,18 @@ func Provider() terraform.ResourceProvider {
if err != nil {
return nil, err
}
_, projectClient, err := apiClient.NewProjectClient()
_, clusterClient, err := apiClient.NewClusterClient()
if err != nil {
return nil, err
}

_, applicationClient, err := apiClient.NewApplicationClient()
if err != nil {
return nil, err
}

_, projectClient, err := apiClient.NewProjectClient()
if err != nil {
return nil, err
}
_, repositoryClient, err := apiClient.NewRepoClient()
if err != nil {
return nil, err
Expand All @@ -141,8 +145,9 @@ func Provider() terraform.ResourceProvider {
}
return initServerInterface(
apiClient,
projectClient,
applicationClient,
clusterClient,
projectClient,
repositoryClient,
repoCredsClient,
)
Expand All @@ -152,8 +157,9 @@ func Provider() terraform.ResourceProvider {

func initServerInterface(
apiClient apiclient.Client,
projectClient project.ProjectServiceClient,
applicationClient application.ApplicationServiceClient,
clusterClient cluster.ClusterServiceClient,
projectClient project.ProjectServiceClient,
repositoryClient repository.RepositoryServiceClient,
repoCredsClient repocreds.RepoCredsServiceClient,
) (interface{}, error) {
Expand All @@ -178,6 +184,7 @@ func initServerInterface(
return ServerInterface{
&apiClient,
&applicationClient,
&clusterClient,
&projectClient,
&repositoryClient,
&repoCredsClient,
Expand Down
109 changes: 109 additions & 0 deletions argocd/resource_argocd_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package argocd

import (
"context"
"fmt"
clusterClient "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"strings"
)

func resourceArgoCDCluster() *schema.Resource {
return &schema.Resource{
Create: resourceArgoCDClusterCreate,
Read: resourceArgoCDClusterRead,
Update: resourceArgoCDClusterUpdate,
Delete: resourceArgoCDClusterDelete,
// TODO: add importer tests
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: clusterSchema(),
}
}

func resourceArgoCDClusterCreate(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
client := *server.ClusterClient
cluster, err := expandCluster(d)
if err != nil {
return fmt.Errorf("could not expand cluster attributes: %s", err)
}
c, err := client.Create(context.Background(), &clusterClient.ClusterCreateRequest{
Cluster: cluster, Upsert: false})
if err != nil {
return fmt.Errorf("something went wrong during cluster resource creation: %s", err)
}
if c.Name != "" {
d.SetId(fmt.Sprintf("%s/%s", c.Server, c.Name))
} else {
d.SetId(c.Server)
}
return resourceArgoCDClusterRead(d, meta)
}

func resourceArgoCDClusterRead(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
client := *server.ClusterClient
c, err := client.Get(context.Background(), getClusterQueryFromID(d))
if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
d.SetId("")
return nil
default:
return fmt.Errorf("could not get cluster information: %s", err)
}
}
err = flattenCluster(c, d)
return err
}

func resourceArgoCDClusterUpdate(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
client := *server.ClusterClient
cluster, err := expandCluster(d)
if err != nil {
return fmt.Errorf("could not expand cluster attributes: %s", err)
}
_, err = client.Update(context.Background(), &clusterClient.ClusterUpdateRequest{Cluster: cluster})
if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
d.SetId("")
return nil
default:
return fmt.Errorf("something went wrong during cluster update: %s", err)
}
}
return resourceArgoCDClusterRead(d, meta)
}

func resourceArgoCDClusterDelete(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
client := *server.ClusterClient
_, err := client.Delete(context.Background(), getClusterQueryFromID(d))
if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
d.SetId("")
return nil
default:
return fmt.Errorf("something went wrong during cluster deletion: %s", err)
}
}
d.SetId("")
return nil
}

func getClusterQueryFromID(d *schema.ResourceData) *clusterClient.ClusterQuery {
id := strings.Split(strings.TrimPrefix(d.Id(), "https://"), "/")
cq := &clusterClient.ClusterQuery{}
if len(id) > 1 {
cq.Name = id[len(id)-1]
cq.Server = fmt.Sprintf("https://%s", strings.Join(id[:len(id)-1], "/"))
} else {
cq.Server = d.Id()
}
return cq
}
148 changes: 148 additions & 0 deletions argocd/resource_argocd_cluster_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package argocd

import (
"fmt"
"runtime"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)

func TestAccArgoCDCluster(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccArgoCDClusterBearerToken(acctest.RandString(10)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"argocd_cluster.simple",
"info.0.connection_state.0.status",
"Successful",
),
resource.TestCheckResourceAttr(
"argocd_cluster.simple",
"shard",
"1",
),
resource.TestCheckResourceAttr(
"argocd_cluster.simple",
"info.0.server_version",
"1.19",
),
resource.TestCheckResourceAttr(
"argocd_cluster.simple",
"info.0.applications_count",
"0",
),
resource.TestCheckResourceAttr(
"argocd_cluster.simple",
"config.0.tls_client_config.0.insecure",
"true",
),
),
},
{
Config: testAccArgoCDClusterTLSCertificate(t, acctest.RandString(10)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"argocd_cluster.tls",
"info.0.connection_state.0.status",
"Successful",
),
resource.TestCheckResourceAttr(
"argocd_cluster.tls",
"info.0.server_version",
"1.19",
),
resource.TestCheckResourceAttr(
"argocd_cluster.tls",
"config.0.tls_client_config.0.insecure",
"false",
),
),
},
},
})
}

func testAccArgoCDClusterBearerToken(clusterName string) string {
return fmt.Sprintf(`
resource "argocd_cluster" "simple" {
server = "https://kubernetes.default.svc.cluster.local"
name = "%s"
shard = "1"
namespaces = ["default", "foo"]
config {
# Uses Kind's bootstrap token whose ttl is 24 hours after cluster bootstrap.
bearer_token = "abcdef.0123456789abcdef"
tls_client_config {
insecure = true
}
}
}
`, clusterName)
}

func testAccArgoCDClusterTLSCertificate(t *testing.T, clusterName string) string {
rc, err := getInternalRestConfig()
if err != nil {
t.Error(err)
}
return fmt.Sprintf(`
resource "argocd_cluster" "tls" {
server = "https://kubernetes.default.svc.cluster.local"
name = "%s"
namespaces = ["bar", "baz"]
config {
tls_client_config {
key_data = <<EOT
%s
EOT
cert_data = <<EOT
%s
EOT
ca_data = <<EOT
%s
EOT
server_name = "%s"
insecure = false
}
}
}
`, clusterName, rc.KeyData, rc.CertData, rc.CAData, rc.ServerName)
}

// getInternalRestConfig returns the internal Kubernetes cluster REST config.
func getInternalRestConfig() (*rest.Config, error) {
rc := &rest.Config{}
var kubeConfigFilePath string

switch runtime.GOOS {
case "windows":
kubeConfigFilePath = fmt.Sprintf("%s\\.kube\\config", homedir.HomeDir())
default:
kubeConfigFilePath = fmt.Sprintf("%s/.kube/config", homedir.HomeDir())
}
cfg, err := clientcmd.LoadFromFile(kubeConfigFilePath)
if err != nil {
return nil, err
}
for key, cluster := range cfg.Clusters {
if key == "kind-argocd" {
authInfo := cfg.AuthInfos[key]
rc.Host = cluster.Server
rc.ServerName = cluster.TLSServerName
rc.TLSClientConfig.CAData = cluster.CertificateAuthorityData
rc.TLSClientConfig.CertData = authInfo.ClientCertificateData
rc.TLSClientConfig.KeyData = authInfo.ClientKeyData
return rc, nil
}
}
return nil, fmt.Errorf("could not find a kind-argocd cluster from the current ~/.kube/config file")
}
Loading

0 comments on commit bc678d4

Please sign in to comment.