Skip to content

Commit

Permalink
Enhance provider to work with XO api token based auth (#314)
Browse files Browse the repository at this point in the history
* Add token argument to provider to facilitate token based auth

Signed-off-by: Dom Del Nano <ddelnano@gmail.com>

* Add test that verifies token auth works and add an example in the docs

Signed-off-by: Dom Del Nano <ddelnano@gmail.com>

* Cover the conflicting auth case in a test

Signed-off-by: Dom Del Nano <ddelnano@gmail.com>

---------

Signed-off-by: Dom Del Nano <ddelnano@gmail.com>
  • Loading branch information
ddelnano authored Mar 15, 2024
1 parent 1fe2442 commit 73353b8
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 23 deletions.
3 changes: 3 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pipeline {
}
stages {
stage('Test') {
environment {
BYPASS_XOA_TOKEN = sh(script: "xo-cli --createToken $XOA_URL $XOA_USER $XOA_PASSWORD | tail -n1", returnStdout: true).trim()
}
steps {
lock('xoa-test-runner') {
sh 'cp /opt/terraform-provider-xenorchestra/testdata/images/alpine-virt-3.17.0-x86_64.iso xoa/testdata/alpine-virt-3.17.0-x86_64.iso'
Expand Down
37 changes: 29 additions & 8 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type Config struct {
Url string
Username string
Password string
Token string
InsecureSkipVerify bool
RetryMode RetryMode
RetryMaxTime time.Duration
Expand All @@ -164,6 +165,7 @@ func GetConfigFromEnv() Config {
var wsURL string
var username string
var password string
var token string
insecure := false
retryMode := None
retryMaxTime := 5 * time.Minute
Expand All @@ -176,6 +178,9 @@ func GetConfigFromEnv() Config {
if v := os.Getenv("XOA_PASSWORD"); v != "" {
password = v
}
if v := os.Getenv("XOA_TOKEN"); v != "" {
token = v
}
if v := os.Getenv("XOA_INSECURE"); v != "" {
insecure = true
}
Expand All @@ -199,6 +204,7 @@ func GetConfigFromEnv() Config {
Url: wsURL,
Username: username,
Password: password,
Token: token,
InsecureSkipVerify: insecure,
RetryMode: retryMode,
RetryMaxTime: retryMaxTime,
Expand All @@ -209,6 +215,16 @@ func NewClient(config Config) (XOClient, error) {
wsURL := config.Url
username := config.Username
password := config.Password
token := config.Token

if token == "" && (username == "" || password == "") {
return nil, fmt.Errorf("One of the following environment variable(s) must be set: XOA_USER and XOA_PASSWORD or XOA_TOKEN")
}

useTokenAuth := false
if token != "" {
useTokenAuth = true
}

tlsConfig := &tls.Config{
InsecureSkipVerify: config.InsecureSkipVerify,
Expand All @@ -226,20 +242,25 @@ func NewClient(config Config) (XOClient, error) {
h = &handler{}
c := jsonrpc2.NewConn(context.Background(), objStream, h)

reqParams := map[string]interface{}{
"email": username,
"password": password,
reqParams := map[string]interface{}{}
if useTokenAuth {
reqParams["token"] = token
} else {

reqParams["email"] = username
reqParams["password"] = password
}
var reply signInResponse
err = c.Call(context.Background(), "session.signInWithPassword", reqParams, &reply)
err = c.Call(context.Background(), "session.signIn", reqParams, &reply)
if err != nil {
return nil, err
}

var token string
err = c.Call(context.Background(), "token.create", map[string]interface{}{}, &token)
if err != nil {
return nil, err
if !useTokenAuth {
err = c.Call(context.Background(), "token.create", map[string]interface{}{}, &token)
if err != nil {
return nil, err
}
}

jar, err := cookiejar.New(&cookiejar.Options{})
Expand Down
13 changes: 11 additions & 2 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ terraform {
xenorchestra = {
source = "vatesfr/xenorchestra"
}
xenorchestra_token_auth = {
source = "vatesfr/xenorchestra"
}
}
}
Expand All @@ -41,19 +44,25 @@ provider "xenorchestra" {
# used sparingly!
insecure = <false|true> # Or set XOA_INSECURE environment variable to any value
}
provider "xenorchestra_token_auth" {
# XOA_USER and XOA_PASSWORD cannot be set, nor can their arguments
token = "<token from XO>" # or set XOA_TOKEN environment variable
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `password` (String) Password for xoa api. Can be set via the XOA_PASSWORD environment variable.
- `url` (String) Hostname of the xoa router. Can be set via the XOA_URL environment variable.
- `username` (String) User account for xoa api. Can be set via the XOA_USER environment variable.

### Optional

- `insecure` (Boolean) Whether SSL should be verified or not. Can be set via the XOA_INSECURE environment variable.
- `password` (String) Password for xoa api. Can be set via the XOA_PASSWORD environment variable.
- `retry_max_time` (String) If `retry_mode` is set, this specifies the duration for which the backoff method will continue retries. Can be set via the `XOA_RETRY_MAX_TIME` environment variable
- `retry_mode` (String) Specifies if retries should be attempted for requests that require eventual . Can be set via the XOA_RETRY_MODE environment variable.
- `token` (String) Password for xoa api. Can be set via the XOA_TOKEN environment variable.
- `username` (String) User account for xoa api. Can be set via the XOA_USER environment variable.
8 changes: 8 additions & 0 deletions examples/provider/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ terraform {
xenorchestra = {
source = "vatesfr/xenorchestra"
}
xenorchestra_token_auth = {
source = "vatesfr/xenorchestra"
}
}
}

Expand All @@ -21,3 +24,8 @@ provider "xenorchestra" {
# used sparingly!
insecure = <false|true> # Or set XOA_INSECURE environment variable to any value
}

provider "xenorchestra_token_auth" {
# XOA_USER and XOA_PASSWORD cannot be set, nor can their arguments
token = "<token from XO>" # or set XOA_TOKEN environment variable
}
1 change: 1 addition & 0 deletions xoa/acc_setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

var testObjectIndex int = 1
var accTestPrefix string = "terraform-acc"
var accTestXoToken string = os.Getenv("BYPASS_XOA_TOKEN")
var accTestPool client.Pool
var accTestHost client.Host
var accDefaultSr client.StorageRepository
Expand Down
48 changes: 48 additions & 0 deletions xoa/data_source_host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,54 @@ func TestAccXenorchestraDataSource_host(t *testing.T) {
)
}

func TestAccXenorchestraDataSource_hostXoTokenAuth(t *testing.T) {
resourceName := "data.xenorchestra_host.host"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccTokenAuthProviders,
Steps: []resource.TestStep{
{
Config: testAccXenorchestraDataSourceHostConfig(accTestHost.NameLabel),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckXenorchestraDataSourceHost(resourceName),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "cpus.cores"),
resource.TestCheckResourceAttrSet(resourceName, "cpus.sockets"),
resource.TestCheckResourceAttrSet(resourceName, "memory"),
resource.TestCheckResourceAttrSet(resourceName, "memory_usage"),
resource.TestCheckResourceAttr(resourceName, "name_label", accTestHost.NameLabel)),
},
},
},
)
}

func TestAccXenorchestraDataSource_hostXoTokenAuthShouldFail(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
// Specify all the parameters to the provider to force the validation
// to fail
Config: `provider xenorchestra {
alias = "token_auth"
username = "test"
password = "test"
token = "token"
}
data "xenorchestra_host" "host" {
provider = xenorchestra.token_auth
name_label = "%s"
}
`,
ExpectError: regexp.MustCompile(`Error: Conflicting configuration arguments`),
},
},
},
)
}

func TestAccXenorchestraDataSource_hostNotFound(t *testing.T) {
resourceName := "data.xenorchestra_host.host"
resource.Test(t, resource.TestCase{
Expand Down
29 changes: 21 additions & 8 deletions xoa/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,27 @@ func Provider() *schema.Provider {
Description: "Hostname of the xoa router. Can be set via the XOA_URL environment variable.",
},
"username": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("XOA_USER", nil),
Description: "User account for xoa api. Can be set via the XOA_USER environment variable.",
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("XOA_USER", nil),
Description: "User account for xoa api. Can be set via the XOA_USER environment variable.",
RequiredWith: []string{"password"},
ConflictsWith: []string{"token"},
},
"password": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("XOA_PASSWORD", nil),
Description: "Password for xoa api. Can be set via the XOA_PASSWORD environment variable.",
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("XOA_PASSWORD", nil),
Description: "Password for xoa api. Can be set via the XOA_PASSWORD environment variable.",
RequiredWith: []string{"username"},
ConflictsWith: []string{"token"},
},
"token": &schema.Schema{
Type: schema.TypeString,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("XOA_TOKEN", nil),
Description: "Password for xoa api. Can be set via the XOA_TOKEN environment variable.",
ConflictsWith: []string{"username", "password"},
},
"insecure": &schema.Schema{
Type: schema.TypeBool,
Expand Down Expand Up @@ -92,6 +103,7 @@ func xoaConfigure(d *schema.ResourceData) (interface{}, error) {
url := d.Get("url").(string)
username := d.Get("username").(string)
password := d.Get("password").(string)
token := d.Get("token").(string)
insecure := d.Get("insecure").(bool)
retryMode := d.Get("retry_mode").(string)
retryMaxTime := d.Get("retry_max_time").(string)
Expand All @@ -110,6 +122,7 @@ func xoaConfigure(d *schema.ResourceData) (interface{}, error) {
Url: url,
Username: username,
Password: password,
Token: token,
InsecureSkipVerify: insecure,
RetryMode: retry,
RetryMaxTime: duration,
Expand Down
40 changes: 35 additions & 5 deletions xoa/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
)

var testAccProviders map[string]*schema.Provider
var testAccTokenAuthProviders map[string]*schema.Provider
var testAccFailToStartAndHaltProviders map[string]*schema.Provider
var testAccFailToDeleteVmProviders map[string]*schema.Provider

var testAccProvider *schema.Provider
var testAccTokenAuthProvider *schema.Provider
var testAccFailToStartHaltVmProvider *schema.Provider
var testAccFailToDeleteVmProvider *schema.Provider

Expand All @@ -22,6 +24,11 @@ func init() {
"xenorchestra": testAccProvider,
}

testAccTokenAuthProvider = createTokenAuthProvider()
testAccTokenAuthProviders = map[string]*schema.Provider{
"xenorchestra": testAccTokenAuthProvider,
}

testAccFailToStartHaltVmProvider = Provider()
testAccFailToStartHaltVmProvider.ConfigureFunc = internal.GetFailToStartAndHaltXOClient
testAccFailToStartAndHaltProviders = map[string]*schema.Provider{
Expand All @@ -34,16 +41,39 @@ func init() {
}
}

func createTokenAuthProvider() *schema.Provider {
provider := Provider()

// The test suite runs in an environment where the XOA_USER and XOA_PASSWORD environment
// variables are set. Therefore the DefaultFunc's and ConflictsWith's will think that
// username, password and token were supplied and will fail validation. The patching
// below allows this test provider to think only token auth is supplied (ConflictsWith changes)
// and prevents the username and password from being passed through (DefaultFunc changes).
var f schema.SchemaDefaultFunc = func() (interface{}, error) { return "", nil }
provider.Schema["username"].DefaultFunc = f
provider.Schema["username"].ConflictsWith = []string{}

provider.Schema["password"].DefaultFunc = f
provider.Schema["password"].ConflictsWith = []string{}

provider.Schema["token"].ConflictsWith = []string{}
provider.Schema["token"].DefaultFunc = schema.EnvDefaultFunc("BYPASS_XOA_TOKEN", nil)
return provider
}

func testAccPreCheck(t *testing.T) {
if v := os.Getenv("XOA_URL"); v == "" {
t.Fatal("The XOA_URL environment variable must be set")
}
if v := os.Getenv("XOA_USER"); v == "" {
t.Fatal("The XOA_USER environment variable must be set")
}
if v := os.Getenv("XOA_PASSWORD"); v == "" {
t.Fatal("The XOA_PASSWORD environment variable must be set")

user := os.Getenv("XOA_USER")
password := os.Getenv("XOA_PASSWORD")
token := os.Getenv("BYPASS_XOA_TOKEN")

if token == "" && (user == "" || password == "") {
t.Fatal("One of the following environment variable(s) must be set: XOA_USER and XOA_PASSWORD or BYPASS_XOA_TOKEN")
}

if v := os.Getenv("XOA_POOL"); v == "" {
t.Fatal("The XOA_POOL environment variable must be set")
}
Expand Down

0 comments on commit 73353b8

Please sign in to comment.