Repository: jfrog/terraform-provider-xray
Pull request #46 (2022-05-04 18:43:35)

Use telemetry and client funcs from shared module


Patch:
diff --git a/go.mod b/go.mod
index 74661dd4..08d0d62e 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/jfrog/terraform-provider-xray
 require (
 	github.com/go-resty/resty/v2 v2.7.0
 	github.com/hashicorp/terraform-plugin-sdk/v2 v2.14.0
-	github.com/jfrog/terraform-provider-shared v0.3.0
+	github.com/jfrog/terraform-provider-shared v0.5.0
 )
 
 require (
@@ -46,12 +46,9 @@ require (
 	github.com/vmihailenco/tagparser v0.1.1 // indirect
 	github.com/zclconf/go-cty v1.10.0 // indirect
 	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
 	golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect
 	golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.10 // indirect
-	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/appengine v1.6.6 // indirect
 	google.golang.org/genproto v0.0.0-20200711021454-869866162049 // indirect
 	google.golang.org/grpc v1.45.0 // indirect
diff --git a/go.sum b/go.sum
index 07feea6f..c56cadaf 100644
--- a/go.sum
+++ b/go.sum
@@ -142,8 +142,8 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/jfrog/terraform-provider-shared v0.3.0 h1:va3toJrofl5W7tAbU7HSV5z65utNAqaCtkqu3GEeFTk=
-github.com/jfrog/terraform-provider-shared v0.3.0/go.mod h1:8QRccISRVo/Ht/e7vjcNhjb49BJSheZ1bfEUwGdgZi8=
+github.com/jfrog/terraform-provider-shared v0.5.0 h1:1TEHe3ZkLSNBcxcQ5Ba9X05WiXJpbScRR7eaeXW00CA=
+github.com/jfrog/terraform-provider-shared v0.5.0/go.mod h1:2ueIlfizwfDAkL1jm3hmvYBqbMusQtHz33ZpDiPOyxA=
 github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
 github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
 github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
@@ -227,15 +227,13 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -294,8 +292,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/xray/provider.go b/pkg/xray/provider.go
index 8cb5e49b..787585b4 100644
--- a/pkg/xray/provider.go
+++ b/pkg/xray/provider.go
@@ -3,20 +3,18 @@ package xray
 import (
 	"context"
 	"fmt"
-	"net/http"
-	"net/url"
-	"regexp"
 
-	"github.com/jfrog/terraform-provider-shared/validator"
-
-	"github.com/go-resty/resty/v2"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+	"github.com/jfrog/terraform-provider-shared/client"
+	"github.com/jfrog/terraform-provider-shared/util"
+	"github.com/jfrog/terraform-provider-shared/validator"
 )
 
 // Version for some reason isn't getting updated by the linker
 var Version = "0.0.1"
+var productId = "terraform-provider-xray/"  + Version
 
 // Provider Xray provider that supports configuration via username+password or a token
 // Supported resources are policies and watches
@@ -26,8 +24,7 @@ func Provider() *schema.Provider {
 			"url": {
 				Type:     schema.TypeString,
 				Optional: true,
-				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"JFROG_URL", "XRAY_URL"},
-					"http://localhost:8081"),
+				DefaultFunc: schema.MultiEnvDefaultFunc([]string{"XRAY_URL", "JFROG_URL"}, "http://localhost:8081"),
 				ValidateFunc: validation.IsURLWithHTTPorHTTPS,
 				Description:  "URL of Artifactory. This can also be sourced from the `XRAY_URL` or `JFROG_URL` environment variable. Default to 'http://localhost:8081' if not set.",
 			},
@@ -41,13 +38,15 @@ func Provider() *schema.Provider {
 			},
 		},
 
-		ResourcesMap: map[string]*schema.Resource{
-			// Xray resources
-			"xray_security_policy": resourceXraySecurityPolicyV2(),
-			"xray_license_policy":  resourceXrayLicensePolicyV2(),
-			"xray_watch":           resourceXrayWatch(),
-			"xray_settings":        resourceXraySettings(),
-		},
+		ResourcesMap: util.AddTelemetry(
+			productId,
+			map[string]*schema.Resource{
+				"xray_security_policy": resourceXraySecurityPolicyV2(),
+				"xray_license_policy":  resourceXrayLicensePolicyV2(),
+				"xray_watch":           resourceXrayWatch(),
+				"xray_settings":        resourceXraySettings(),
+			},
+		),
 	}
 
 	p.ConfigureContextFunc = func(ctx context.Context, data *schema.ResourceData) (interface{}, diag.Diagnostics) {
@@ -55,130 +54,38 @@ func Provider() *schema.Provider {
 		if terraformVersion == "" {
 			terraformVersion = "0.13+compatible"
 		}
-		configuration, err := providerConfigure(data, terraformVersion)
-		return configuration, diag.FromErr(err)
+		return providerConfigure(ctx, data, terraformVersion)
 	}
 
 	return p
 }
 
-func buildResty(URL string) (*resty.Client, error) {
-
-	u, err := url.ParseRequestURI(URL)
-
-	if err != nil {
-		return nil, err
-	}
-	baseUrl := fmt.Sprintf("%s://%s", u.Scheme, u.Host)
-	restyBase := resty.New().SetHostURL(baseUrl).OnAfterResponse(func(client *resty.Client, response *resty.Response) error {
-		if response == nil {
-			return fmt.Errorf("no response found")
-		}
-		if response.StatusCode() >= http.StatusBadRequest {
-			return fmt.Errorf("\n%d %s %s\n%s", response.StatusCode(), response.Request.Method, response.Request.URL, string(response.Body()[:]))
-		}
-		return nil
-	}).
-		SetHeader("content-type", "application/json").
-		SetHeader("accept", "*/*").
-		SetHeader("user-agent", "jfrog/terraform-provider-xray:"+Version).
-		SetRetryCount(5)
-	restyBase.DisableWarn = true
-	return restyBase, nil
-
-}
-
-func addAuthToResty(client *resty.Client, accessToken string) (*resty.Client, error) {
-	if accessToken != "" {
-		return client.SetAuthToken(accessToken), nil
-	}
-	return nil, fmt.Errorf("no authentication details supplied")
-}
-
 // Creates the client for artifactory, will use token auth
-func providerConfigure(d *schema.ResourceData, terraformVersion string) (interface{}, error) {
+func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVersion string) (interface{}, diag.Diagnostics) {
 	URL, ok := d.GetOk("url")
 	if URL == nil || URL == "" || !ok {
-		return nil, fmt.Errorf("you must supply a URL")
+		return nil, diag.Errorf("you must supply a URL")
 	}
 
-	restyBase, err := buildResty(URL.(string))
+	restyBase, err := client.Build(URL.(string), Version)
 	if err != nil {
-		return nil, err
+		return nil, diag.FromErr(err)
 	}
 	accessToken := d.Get("access_token").(string)
 
-	restyBase, err = addAuthToResty(restyBase, accessToken)
+	restyBase, err = client.AddAuth(restyBase, "", accessToken)
 	if err != nil {
-		return nil, err
+		return nil, diag.FromErr(err)
 	}
 
-	_, err = sendUsageRepo(restyBase, terraformVersion)
-	if err != nil {
-		return nil, err
+	licenseErr := util.CheckArtifactoryLicense(restyBase)
+	if licenseErr != nil {
+		return nil, licenseErr
 	}
 
-	err = checkArtifactoryLicense(restyBase)
-	if err != nil {
-		return nil, err
-	}
+	featureUsage := fmt.Sprintf("Terraform/%s", terraformVersion)
+	util.SendUsage(ctx, restyBase, productId, featureUsage)
 
 	return restyBase, nil
 
 }
-
-func sendUsageRepo(restyBase *resty.Client, terraformVersion string) (interface{}, error) {
-	type Feature struct {
-		FeatureId string `json:"featureId"`
-	}
-	type UsageStruct struct {
-		ProductId string    `json:"productId"`
-		Features  []Feature `json:"features"`
-	}
-	_, err := restyBase.R().SetBody(UsageStruct{
-		"terraform-provider-xray/" + Version,
-		[]Feature{
-			{FeatureId: "Partner/ACC-007450"},
-			{FeatureId: "Terraform/" + terraformVersion},
-		},
-	}).Post("artifactory/api/system/usage")
-
-	if err != nil {
-		return nil, fmt.Errorf("unable to report usage %s", err)
-	}
-	return nil, nil
-}
-
-func checkArtifactoryLicense(client *resty.Client) error {
-
-	type License struct {
-		Type string `json:"type"`
-	}
-
-	type LicensesWrapper struct {
-		License
-		Licenses []License `json:"licenses"` // HA licenses returns as an array instead
-	}
-
-	licensesWrapper := LicensesWrapper{}
-	_, err := client.R().
-		SetResult(&licensesWrapper).
-		Get("/artifactory/api/system/license")
-
-	if err != nil {
-		return fmt.Errorf("Failed to check for license. %s", err)
-	}
-
-	var licenseType string
-	if len(licensesWrapper.Licenses) > 0 {
-		licenseType = licensesWrapper.Licenses[0].Type
-	} else {
-		licenseType = licensesWrapper.Type
-	}
-
-	if matched, _ := regexp.MatchString(`Enterprise`, licenseType); !matched {
-		return fmt.Errorf("Xray requires Enterprise license!")
-	}
-
-	return nil
-}


Test patch:
diff --git a/pkg/xray/provider_test.go b/pkg/xray/provider_test.go
index 9fee1f8f..86db909f 100644
--- a/pkg/xray/provider_test.go
+++ b/pkg/xray/provider_test.go
@@ -12,6 +12,7 @@ import (
 
 	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
 	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+	"github.com/jfrog/terraform-provider-shared/client"
 )
 
 func testAccProviders() map[string]func() (*schema.Provider, error) {
@@ -29,15 +30,25 @@ func TestProvider(t *testing.T) {
 }
 
 func getTestResty(t *testing.T) *resty.Client {
-	if v := os.Getenv("JFROG_URL"); v == "" {
-		t.Error("JFROG_URL must be set for acceptance tests")
+	var ok bool
+	var xrayUrl string
+	if xrayUrl, ok = os.LookupEnv("XRAY_URL"); !ok {
+		if xrayUrl, ok = os.LookupEnv("JFROG_URL"); !ok {
+			t.Fatal("XRAY_URL or JFROG_URL must be set for acceptance tests")
+		}
 	}
-	restyClient, err := buildResty(os.Getenv("JFROG_URL"))
+	restyClient, err := client.Build(xrayUrl, "")
 	if err != nil {
 		t.Error(err)
 	}
-	accessToken := os.Getenv("XRAY_ACCESS_TOKEN")
-	restyClient, err = addAuthToResty(restyClient, accessToken)
+
+	var accessToken string
+	if accessToken, ok = os.LookupEnv("XRAY_ACCESS_TOKEN"); !ok {
+		if accessToken, ok = os.LookupEnv("JFROG_ACCESS_TOKEN"); !ok {
+			t.Fatal("XRAY_ACCESS_TOKEN or JFROG_ACCESS_TOKEN must be set for acceptance tests")
+		}
+	}
+	restyClient, err = client.AddAuth(restyClient, "", accessToken)
 	if err != nil {
 		t.Error(err)
 	}