Skip to content

Commit

Permalink
Add external client
Browse files Browse the repository at this point in the history
  • Loading branch information
micahkemp-splunk committed Jun 3, 2022
1 parent 79dffc3 commit b15c4fe
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 1 deletion.
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ Below arguments for the provider can also be set as environment variables.
If specified, auth token takes priority over username/password.
* `insecure_skip_verify` or `SPLUNK_INSECURE_SKIP_VERIFY` - (Optional) Insecure skip verification flag (Defaults to `true`)
* `timeout` or `SPLUNK_TIMEOUT` - (Optional) Timeout when making calls to Splunk server. (Defaults to `60 seconds`)
* `use_legacy_client_default` - (Default true) Determines the default behavior for resources that implement use_legacy_client.
Currently defaults to true, but will default to false in a future version.
The legacy client is being replaced by a standalone Splunk client with improved error and drift handling. The legacy client will be deprecated in a future version.

(NOTE: Auth token can only be used with certain type of Splunk deployments.
Read more on authentication with tokens here: https://docs.splunk.com/Documentation/Splunk/latest/Security/Setupauthenticationwithtokens)
57 changes: 56 additions & 1 deletion splunk/provider.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package splunk

import (
"fmt"
"net/url"
"time"

"github.com/splunk/terraform-provider-splunk/client"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/splunk/go-splunk-client/pkg/authenticators"
externalclient "github.com/splunk/go-splunk-client/pkg/client"
)

type SplunkProvider struct {
Client *client.Client
Client *client.Client
ExternalClient *externalclient.Client
useLegacyClientDefault bool
}

func Provider() terraform.ResourceProvider {
Expand Down Expand Up @@ -65,6 +71,14 @@ func providerSchema() map[string]*schema.Schema {
DefaultFunc: schema.EnvDefaultFunc("SPLUNK_TIMEOUT", 60),
Description: "Timeout when making calls to Splunk server. Defaults to 60 seconds",
},
"use_legacy_client_default": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Determines the default behavior for resources that implement use_legacy_client. " +
"Currently defaults to true, but will default to false in a future version. " +
"The legacy client is being replaced by a standalone Splunk client with improved error and drift handling. The legacy client will be deprecated in a future version.",
},
}
}

Expand Down Expand Up @@ -97,20 +111,49 @@ func providerResources() map[string]*schema.Resource {
}
}

// canonicalizeSplunkURL returns a URL string from originalURL that includes a default https scheme.
// go-splunk-client requires a full URL, but this provider has historically permitted the URL to be missing
// its scheme.
func canonicalizeSplunkURL(originalURL string) (string, error) {
externalClientURL := originalURL

parsedURL, err := url.Parse(externalClientURL)
if err != nil || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") {
externalClientURL = fmt.Sprintf("https://%s", externalClientURL)
if _, err := url.Parse(externalClientURL); err != nil {
return "", fmt.Errorf("splunk: unable to determine valid splunkd URL from %q", originalURL)
}
}

return externalClientURL, nil
}

// This is the function used to fetch the configuration params given
// to our provider which we will use to initialise splunk client that
// interacts with the API.
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
provider := &SplunkProvider{}
var splunkdClient *client.Client

externalClientURL, err := canonicalizeSplunkURL(d.Get("url").(string))
if err != nil {
return nil, err
}

externalClient := externalclient.Client{
URL: externalClientURL,
}

httpClient, err := client.NewSplunkdHTTPClient(
time.Duration(d.Get("timeout").(int))*time.Second,
d.Get("insecure_skip_verify").(bool))
if err != nil {
return nil, err
}

externalClient.TLSInsecureSkipVerify = d.Get("insecure_skip_verify").(bool)
externalClient.Timeout = time.Duration(d.Get("timeout").(int)) * time.Second

if token, ok := d.GetOk("auth_token"); ok {
splunkdClient, err = client.NewSplunkdClientWithAuthToken(token.(string),
[2]string{d.Get("username").(string), d.Get("password").(string)},
Expand All @@ -119,6 +162,10 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
if err != nil {
return splunkdClient, err
}

externalClient.Authenticator = authenticators.Token{
Token: token.(string),
}
} else {
splunkdClient, err = client.NewSplunkdClient("",
[2]string{d.Get("username").(string), d.Get("password").(string)},
Expand All @@ -132,8 +179,16 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
if err != nil {
return splunkdClient, err
}

externalClient.Authenticator = &authenticators.Password{
Username: d.Get("username").(string),
Password: d.Get("password").(string),
}
}

provider.Client = splunkdClient
provider.ExternalClient = &externalClient
provider.useLegacyClientDefault = d.Get("use_legacy_client_default").(bool)

return provider, nil
}
67 changes: 67 additions & 0 deletions splunk/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/splunk/go-splunk-client/pkg/authenticators"
externalClient "github.com/splunk/go-splunk-client/pkg/client"
"github.com/splunk/terraform-provider-splunk/client"
)

Expand Down Expand Up @@ -40,6 +42,23 @@ func newTestClient() (*client.Client, error) {
http)
}

func newTestExternalClient() (*externalClient.Client, error) {
externalURL, err := canonicalizeSplunkURL(os.Getenv("SPLUNK_URL"))
if err != nil {
return nil, err
}

return &externalClient.Client{
URL: externalURL,
Authenticator: &authenticators.Password{
Username: os.Getenv("SPLUNK_USERNAME"),
Password: os.Getenv("SPLUNK_PASSWORD"),
},
TLSInsecureSkipVerify: true,
Timeout: 30 * time.Second,
}, nil
}

func testAccPreCheck(t *testing.T) {
if v := os.Getenv("SPLUNK_HOME"); v == "" {
t.Fatal("SPLUNK_HOME must be set for acceptance tests")
Expand All @@ -54,3 +73,51 @@ func testAccPreCheck(t *testing.T) {
t.Fatal("SPLUNK_PASSWORD must be set for acceptance tests")
}
}

func TestProvider_canonicalizeSplunkURL(t *testing.T) {
tests := []struct {
name string
input string
want string
wantError bool
}{
{
name: "local",
input: "localhost:8089",
want: "https://localhost:8089",
},
{
name: "domain, port 8089",
input: "splunk.example.com:8089",
want: "https://splunk.example.com:8089",
},
{
name: "domain, no port",
input: "splunk.example.com",
want: "https://splunk.example.com",
},
{
name: "local with scheme",
input: "https://localhost:8089",
want: "https://localhost:8089",
},
{
name: "domain with scheme",
input: "https://splunk.example.com:8089",
want: "https://splunk.example.com:8089",
},
}

for _, test := range tests {
got, err := canonicalizeSplunkURL(test.input)
gotError := err != nil

if gotError != test.wantError {
t.Errorf("%s: canonicalizeSplunkURL() returned error? %v (%s)", test.name, gotError, err)
}

if got != test.want {
t.Errorf("%s: canonicalizeSplunkURL() got\n%s, want\n%s", test.name, got, test.want)
}
}
}

0 comments on commit b15c4fe

Please sign in to comment.