diff --git a/.goreleaser.yml b/.goreleaser.yml
index 4b9bb55..927232c 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -2,7 +2,7 @@
# behavior.
version: 2
env:
- - PROVIDER_VERSION=4.1.0
+ - PROVIDER_VERSION=4.2.0
before:
hooks:
# this is just an example and not a requirement for provider building/publishing
diff --git a/GNUmakefile b/GNUmakefile
index b12bb3f..fe61116 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -3,7 +3,7 @@ HOSTNAME=delphix.com
NAMESPACE=dct
NAME=delphix
BINARY=terraform-provider-${NAME}
-VERSION=4.1.0
+VERSION=4.2.0
OS_ARCH=darwin_arm64
default: install
diff --git a/docs/index.md b/docs/index.md
index ea3d9fd..a328456 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -40,7 +40,7 @@ terraform {
required_providers {
delphix = {
source = "delphix-integrations/delphix"
- version = "4.1.0"
+ version = "4.2.0"
}
}
}
@@ -82,4 +82,5 @@ Consult the Resources section for details on individual resources, such as VDB,
| delphix_oracle_dsource update
delphix_oracle_dsource import | v 3.4.1 | v 2025.1.2 |
| delphix_appdata_dsource update
delphix_appdata_dsource import | v 4.0.0 | v 2025.2.0 |
| delphix_environment update
delphix_environment import | v 4.0.0 | v 2025.2.0 |
-| delphix_vdb_group tag management
delphix_vdb_group import | v 4.1.0 | v 2025.3.0 |
\ No newline at end of file
+| delphix_vdb_group tag management
delphix_vdb_group import | v 4.1.0 | v 2025.3.0 |
+| delphix_engine_configuration
delphix_engine_dct_registration | v 4.2.0 | v 2025.6.0 |
\ No newline at end of file
diff --git a/examples/engine_config/main.tf b/examples/engine_config/main.tf
index 4d606ca..30f7608 100644
--- a/examples/engine_config/main.tf
+++ b/examples/engine_config/main.tf
@@ -5,7 +5,7 @@
terraform {
required_providers {
delphix = {
- version = "3.3.0-beta"
+ version = "4.2.0"
source = "delphix.com/dct/delphix"
}
}
@@ -13,10 +13,11 @@ terraform {
provider "delphix" {
tls_insecure_skip = true
- key = "1.jTElhpXIao7pTNzVCYdkj1HpGXriTBlYbPha1Di8HjvMF6nESA1crkGlljowDs7y"
- host = "ubuntu-2-uv49-qar-125346-27a4593a.dlpxdc.co"
+ key = "1.XXXX"
+ host = "HOSTNAME"
}
+/* BLOCK STORAGE */
resource "delphix_engine_configuration" "config" {
engine_host = "http://eg22.dlpxdc.co"
api_version = "1.11.31"
@@ -26,5 +27,135 @@ resource "delphix_engine_configuration" "config" {
password = "xxx"
email = "noreply@delphix.com"
engine_type = "CD"
+ device_type = "BLOCK"
}
+/* BLOCK STORAGE With NTP configuration */
+resource "delphix_engine_configuration" "config" {
+ engine_host = "http://eg22.dlpxdc.co"
+ api_version = "1.11.31"
+ sys_user = "XXXX"
+ sys_password = "XXXX"
+ user = "XXXX"
+ password = "XXXX"
+ email = "noreply@delphix.com"
+ engine_type = "CD"
+ device_type = "BLOCK"
+ ntp_timezone = "America/Anchorage"
+ ntp_servers = ["Europe.pool.ntp.org"]
+}
+
+/* OBJECT STORAGE with ROLE based configurations*/
+resource "delphix_engine_configuration" "config2" {
+ engine_host = "http://object.dlpxdc.co"
+ api_version = "1.11.46"
+ sys_user = "XXXX"
+ sys_password = "XXXX"
+ user = "XXXX"
+ password = "XXXX"
+ email = "no-reply@delphix.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+ object_storage_params {
+ auth_type = "ROLE"
+ region = "us-west-2"
+ bucket = "dcoa-prod-object"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "30GB"
+ }
+ ntp_timezone = "Africa/Asmera"
+ ntp_servers = ["Europe.pool.ntp.org"]
+}
+
+/* OBJECT STORAGE with ACCESS_KEY based configuration*/
+resource "delphix_engine_configuration" "config2" {
+ engine_host = "http://object.dlpxdc.co"
+ api_version = "1.11.46"
+ sys_user = "XXXX"
+ sys_password = "XXXX"
+ user = "XXXX"
+ password = "XXXX"
+ email = "no-reply@delphix.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+ object_storage_params {
+ auth_type = "ACCESS_KEY"
+ region = "us-west-2"
+ bucket = "dcoa-prod-object"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "30GB"
+ access_id = "XXXX"
+ access_key = "XXXX"
+ }
+ ntp_timezone = "Africa/Asmera"
+ ntp_servers = ["Europe.pool.ntp.org"]
+}
+
+/*SMTP, NTP, DNS, WEB PROXY, USER ANALYTICS, PHONEHOME CONFIGS*/
+resource "delphix_engine_configuration" "config2" {
+ engine_host = "http://object.dlpxdc.co"
+ api_version = "1.11.46"
+ sys_user = "XXXX"
+ sys_password = "XXXX"
+ user = "XXXX"
+ password = "XXXX"
+ email = "no-reply@delphix.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+ object_storage_params {
+ auth_type = "ROLE"
+ region = "us-west-2"
+ bucket = "dcoa-prod-object"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "30GB"
+ }
+ ntp_timezone = "Africa/Asmera"
+ ntp_servers = ["Europe.pool.ntp.org"]
+ smtp_config {
+ server = "delphix.com"
+ port = 25
+ from_email_address = "noreply@perforce.com"
+ send_timeout = 80
+ tls_authentication = true
+ }
+ dns_config {
+ servers = ["172.16.105.23","172.16.105.24"]
+ domains = ["perforce.com","delphix.com"]
+ }
+ phone_home_enabled = true
+
+ web_proxy_config {
+ host = "delphix.com"
+ port = 8081
+ }
+ user_analytics_enabled = true
+}
+
+/* SSO Config */
+resource "delphix_engine_configuration" "config2" {
+ engine_host = "http://object.dlpxdc.co"
+ api_version = "1.11.46"
+ sys_user = "XXXX"
+ sys_password = "XXXX"
+ user = "XXXX"
+ password = "XXXX"
+ email = "no-reply@delphix.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+ object_storage_params {
+ auth_type = "ROLE"
+ region = "us-west-2"
+ bucket = "dcoa-prod-object"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "30GB"
+ }
+
+ sso_config {
+ enabled=true
+ response_skew_time=120
+ max_authentication_age=86400
+ saml_metadata = <
+ EOF
+ }
+}
\ No newline at end of file
diff --git a/examples/engine_register/main.tf b/examples/engine_register/main.tf
index d337894..e7fc1a3 100644
--- a/examples/engine_register/main.tf
+++ b/examples/engine_register/main.tf
@@ -1,7 +1,7 @@
terraform {
required_providers {
delphix = {
- version = "3.3.0-beta"
+ version = "4.2.0"
source = "delphix.com/dct/delphix"
}
}
@@ -9,11 +9,11 @@ terraform {
provider "delphix" {
tls_insecure_skip = true
- key = "1.jTElhpXIao7pTNzVCYdkj1HpGXriTBlYbPha1Di8HjvMF6nESA1crkGlljowDs7y"
- host = "ubuntu-2-uv49-qar-125346-27a4593a.dlpxdc.co"
+ key = "1.XXXX"
+ host = "HOSTNAME"
}
-resource "delphix_engine_registration" "register" {
+resource "delphix_engine_dct_registration" "register" {
hostname = "eg21.dlpxdc.co"
name = "test_tf"
username = "xxx"
diff --git a/internal/provider/engine_api.go b/internal/provider/engine_api.go
index 2324e39..131866f 100644
--- a/internal/provider/engine_api.go
+++ b/internal/provider/engine_api.go
@@ -5,8 +5,10 @@ import (
"context"
"encoding/json"
"errors"
+ "fmt"
"io"
"net/http"
+ "regexp"
"strconv"
"strings"
@@ -19,8 +21,8 @@ func startSession(ctx context.Context, client *http.Client, engine_host string,
major, _ := strconv.Atoi(versionParts[0])
minor, _ := strconv.Atoi(versionParts[1])
micro, _ := strconv.Atoi(versionParts[2])
- tflog.Error(ctx, DLPX+INFO+"start session for "+engine_host+" version "+version)
- sessionURL := engine_host + "/resources/json/delphix/session"
+ tflog.Info(ctx, DLPX+INFO+"start session for "+engine_host+" version "+version)
+ sessionURL := engine_host + ENGINE_APIS["SESSION"]
apisessionData := APISession{
Version: APIVersion{
Minor: minor,
@@ -61,33 +63,34 @@ func startSession(ctx context.Context, client *http.Client, engine_host string,
}
func login(ctx context.Context, client *http.Client, engine_host string, user string, password string, target string) error {
- loginURL := engine_host + "/resources/json/delphix/login"
+ loginURL := engine_host + ENGINE_APIS["LOGIN"]
loginData := LoginRequest{
Password: password,
Type: "LoginRequest",
Username: user,
Target: target,
}
+ tflog.Info(ctx, DLPX+INFO+"Logging in user "+user+" to target "+target)
loginJSON, err := json.Marshal(loginData)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error marshalling login data: "+err.Error())
return err
}
-
+ tflog.Info(ctx, DLPX+INFO+"Login Payload: "+string(loginJSON))
req, err := http.NewRequest(http.MethodPost, loginURL, bytes.NewReader(loginJSON))
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error creating login request: "+err.Error())
return err
}
req.Header.Set("Content-Type", "application/json")
-
+ tflog.Info(ctx, DLPX+INFO+"Sending login request to "+loginURL)
resp, err := client.Do(req)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error authenticating: "+err.Error())
return err
}
defer resp.Body.Close()
-
+ tflog.Info(ctx, DLPX+INFO+"Received login response with status code "+strconv.Itoa(resp.StatusCode))
// Check for successful login (can be modified to return response body)
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
@@ -98,7 +101,7 @@ func login(ctx context.Context, client *http.Client, engine_host string, user st
}
func getDevices(ctx context.Context, client *http.Client, engine_host string) ([]byte, error) {
- deviceURL := engine_host + "/resources/json/delphix/storage/device"
+ deviceURL := engine_host + ENGINE_APIS["STORAGE_DEVICE"]
req, err := http.NewRequest(http.MethodGet, deviceURL, nil)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error creating device request: "+err.Error())
@@ -122,7 +125,7 @@ func getDevices(ctx context.Context, client *http.Client, engine_host string) ([
}
func getCurrentUser(ctx context.Context, client *http.Client, engine_host string) ([]byte, error) {
- userURL := engine_host + "/resources/json/delphix/user/current"
+ userURL := engine_host + ENGINE_APIS["USER"] + "current"
req, err := http.NewRequest(http.MethodGet, userURL, nil)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error creating current user request: "+err.Error())
@@ -145,23 +148,86 @@ func getCurrentUser(ctx context.Context, client *http.Client, engine_host string
return body, nil
}
-func initializeSystem(ctx context.Context, client *http.Client, engine_host string, resultList ListResult, user string, email string, password string) ([]byte, error) {
- initializeSystemURL := engine_host + "/resources/json/delphix/domain/initializeSystem"
- initializationParams := SystemInitializationParameters{
- Type: "SystemInitializationParameters",
- DefaultUser: user,
- DefaultPassword: password,
- Devices: make([]string, 0),
- }
- if email != "" {
- initializationParams.DefaultEmail = email
- }
+func initializeSystem(ctx context.Context, client *http.Client, engine_host string,
+ resultList ListResult, params InitializationParameters) ([]byte, error) {
+
+ initializeSystemURL := engine_host + ENGINE_APIS["SYSTEM_INITIALIZATION"]
+ var deviceRefs []string
for _, device := range resultList.Result {
if !device.Configured {
- initializationParams.Devices = append(initializationParams.Devices, device.Reference)
+ deviceRefs = append(deviceRefs, device.Reference)
}
}
+
+ tflog.Info(ctx, DLPX+INFO+"Unconfigured Devices: "+fmt.Sprintf("%v", deviceRefs))
+ var email string
+ var initializationParams interface{}
+ if params.Email != "" {
+ email = params.Email
+ }
+
+ if params.DeviceType == OBJECT {
+ var objectStorage *ObjectStore
+ sizeInBytes, err := convertStorageToBytes(params.Size)
+ tflog.Info(ctx, DLPX+INFO+"Converted size in bytes: "+strconv.FormatInt(int64(sizeInBytes), 10))
+ if err != nil {
+ return nil, err
+ }
+
+ objectStorage = &ObjectStore{
+ Type: "S3ObjectStore",
+ Size: sizeInBytes,
+ CacheDevices: deviceRefs,
+ Endpoint: params.Endpoint,
+ Region: params.Region,
+ Bucket: params.Bucket,
+ }
+
+ switch params.AuthType {
+ case ROLE:
+ objectStorage.AccessCredentials = &ObjectStoreAccessCredentials{
+ Type: params.S3_INSTANCE_PROFILE,
+ }
+ case ACCESS_KEY:
+ objectStorage.AccessCredentials = &ObjectStoreAccessCredentials{
+ Type: "S3ObjectStoreAccessKey",
+ ACCESS_ID: params.ACCESS_ID,
+ ACCESS_KEY: params.ACCESS_KEY,
+ }
+ }
+ initializationParams = SystemInitializationObjectStore{
+ Type: "SystemInitializationParameters",
+ DefaultUser: params.User,
+ DefaultPassword: params.Password,
+ DefaultEmail: email,
+ ObjectStore: objectStorage,
+ }
+ resBody, er := testConnectionForObjectStore(ctx, client, engine_host, params)
+ tflog.Info(ctx, DLPX+INFO+"test connection response body: "+string(resBody))
+ if er != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error testing object store connection: "+er.Error())
+ return nil, er
+ }
+ bodyStr := string(resBody)
+ if strings.Contains(bodyStr, `"status":"ERROR"`) {
+ tflog.Error(ctx, DLPX+ERROR+"API returned error response: "+bodyStr)
+ return nil, fmt.Errorf("initialization failed: %s", bodyStr)
+ }
+ tflog.Info(ctx, DLPX+INFO+"test conncection to object store successful.")
+
+ } else {
+ devices := deviceRefs
+ initializationParams = SystemInitializationBlockStorage{
+ Type: "SystemInitializationParameters",
+ DefaultUser: params.User,
+ DefaultPassword: params.Password,
+ DefaultEmail: email,
+ Devices: devices,
+ }
+
+ }
+
postData, err := json.Marshal(initializationParams)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error marshalling initialization parameters: "+err.Error())
@@ -187,11 +253,17 @@ func initializeSystem(ctx context.Context, client *http.Client, engine_host stri
tflog.Error(ctx, DLPX+ERROR+"error reading initializing system response: "+err.Error())
return nil, err
}
+
+ bodyStr := string(body)
+ if strings.Contains(bodyStr, `"status":"ERROR"`) {
+ tflog.Error(ctx, DLPX+ERROR+"API returned error response: "+bodyStr)
+ return nil, fmt.Errorf("initialization failed: %s", bodyStr)
+ }
return body, nil
}
func getAction(ctx context.Context, client *http.Client, engine_host string, action_id string) ([]byte, error) {
- actionURL := engine_host + "/resources/json/delphix/action/" + action_id
+ actionURL := engine_host + ENGINE_APIS["ACTION"] + action_id
req, err := http.NewRequest(http.MethodGet, actionURL, nil)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error creating action request: "+err.Error())
@@ -215,7 +287,7 @@ func getAction(ctx context.Context, client *http.Client, engine_host string, act
}
func getSystem(ctx context.Context, client *http.Client, engine_host string) ([]byte, error) {
- actionURL := engine_host + "/resources/json/delphix/system"
+ actionURL := engine_host + ENGINE_APIS["SYSTEM_INFO"]
req, err := http.NewRequest(http.MethodGet, actionURL, nil)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+"error creating system request: "+err.Error())
@@ -239,7 +311,7 @@ func getSystem(ctx context.Context, client *http.Client, engine_host string) ([]
}
func updatePassword(ctx context.Context, client *http.Client, engine_host string, user_id string, password string) ([]byte, error) {
- updateURL := engine_host + "/resources/json/delphix/user/" + user_id + "/updateCredential"
+ updateURL := engine_host + ENGINE_APIS["USER"] + user_id + "/updateCredential"
// Create credential update parameters
UpdateParameters := CredentialUpdateParameters{
Type: "CredentialUpdateParameters",
@@ -279,34 +351,39 @@ func updatePassword(ctx context.Context, client *http.Client, engine_host string
return body, nil
}
-func createOrUpdateUser(ctx context.Context, client *http.Client, engine_host string, user_name string, password string, user_type string) ([]byte, error) {
- updateURL := engine_host + "/resources/json/delphix/user"
- // Create user parameters
- UserParameters := User{
- Name: user_name,
- UserType: user_type,
- AuthenticationType: "NATIVE",
- Credential: Credential{Password: password, Type: "PasswordCredential"},
- AllowPasswordAuthentication: true,
- Type: "User",
- }
+func setEngieType(ctx context.Context, client *http.Client, engine_host string, engine_type string) ([]byte, error) {
+ updateURL := engine_host + ENGINE_APIS["SYSTEM_INFO"]
+ var eg_type string
- UserParametersJSON, err := json.Marshal(UserParameters)
+ switch engine_type {
+ case "CC":
+ eg_type = "MASKING"
+ case "CD":
+ eg_type = "VIRTUALIZATION"
+ default:
+ tflog.Error(ctx, DLPX+ERROR+"Unknown engine type: "+engine_type)
+ }
+ // Prepare system information data
+ data := SystemInfo{
+ Type: "SystemInfo",
+ EngineType: eg_type,
+ }
+ systemInfoJSON, err := json.Marshal(data)
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error marshalling user data: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error marshalling login data: "+err.Error())
return nil, err
}
- req, err := http.NewRequest(http.MethodPost, updateURL, bytes.NewReader(UserParametersJSON))
+ req, err := http.NewRequest(http.MethodPost, updateURL, bytes.NewReader(systemInfoJSON))
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error creating user request: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error creating request: "+err.Error())
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error create/update user: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error authenticating: "+err.Error())
return nil, err
}
defer resp.Body.Close()
@@ -319,47 +396,235 @@ func createOrUpdateUser(ctx context.Context, client *http.Client, engine_host st
return body, nil
}
-func setEngieType(ctx context.Context, client *http.Client, engine_host string, engine_type string) ([]byte, error) {
- updateURL := engine_host + "/resources/json/delphix/system"
- var eg_type string
-
- switch engine_type {
- case "CC":
- eg_type = "MASKING"
- case "CD":
- eg_type = "VIRTUALIZATION"
- default:
- tflog.Error(ctx, DLPX+ERROR+"Unknown engine type: "+engine_type)
- }
- // Prepare system information data
- data := SystemInfo{
- Type: "SystemInfo",
- EngineType: eg_type,
+func testConnectionForObjectStore(ctx context.Context, client *http.Client, engine_host string, params InitializationParameters) ([]byte, error) {
+ testConnectionURL := engine_host + ENGINE_APIS["OBJECT_STORE_TEST_CONNECTION"]
+ var payload TestConnection
+ if params.AuthType == ACCESS_KEY {
+ payload = TestConnection{
+ Type: "S3ObjectStoreTest",
+ Endpoint: params.Endpoint,
+ Region: params.Region,
+ Bucket: params.Bucket,
+ AccessCredentials: ObjectStoreAccessCredentials{
+ Type: "S3ObjectStoreAccessKey",
+ ACCESS_ID: params.ACCESS_ID,
+ ACCESS_KEY: params.ACCESS_KEY,
+ },
+ }
+ } else {
+ payload = TestConnection{
+ Type: "S3ObjectStoreTest",
+ Endpoint: params.Endpoint,
+ Region: params.Region,
+ Bucket: params.Bucket,
+ AccessCredentials: ObjectStoreAccessCredentials{
+ Type: params.S3_INSTANCE_PROFILE,
+ },
+ }
}
- systemInfoJSON, err := json.Marshal(data)
+ payloadJSON, err := json.Marshal(payload)
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error marshalling login data: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error marshalling test connection data: "+err.Error())
return nil, err
}
-
- req, err := http.NewRequest(http.MethodPost, updateURL, bytes.NewReader(systemInfoJSON))
+ req, err := http.NewRequest(http.MethodPost, testConnectionURL, bytes.NewReader(payloadJSON))
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error creating request: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error creating test connection request: "+err.Error())
return nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
+ tflog.Info(ctx, DLPX+INFO+"test connection response status: "+fmt.Sprintf("%v", resp))
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error authenticating: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error testing connection: "+err.Error())
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
- tflog.Error(ctx, DLPX+ERROR+"error reading response: "+err.Error())
+ tflog.Error(ctx, DLPX+ERROR+"error reading test connection response: "+err.Error())
return nil, err
}
+
+ var testResult TestConnectionResult
+ if unmarshallErr := json.Unmarshal(body, &testResult); unmarshallErr == nil {
+ if testResult.Status == "OK" && testResult.Result.Result {
+ // Test connection successful
+ tflog.Info(ctx, DLPX+INFO+"Object store connection test successful")
+ return body, nil
+ } else if testResult.Status == "OK" && !testResult.Result.Result {
+ // Test connection failed with specific error
+ tflog.Error(ctx, DLPX+ERROR+"Object store connection test failed: "+testResult.Result.ErrorMessage)
+ return nil, fmt.Errorf("object store connection test failed: %s", testResult.Result.ErrorMessage)
+ } else if testResult.Status == "ERROR" {
+ // API level error
+ tflog.Error(ctx, DLPX+ERROR+"API error during connection test: "+string(body))
+ return nil, fmt.Errorf("API error during connection test: %s", string(body))
+ }
+ }
return body, nil
}
+
+// convertStorageToBytes converts storage size strings like "20TB", "500GB", "1.5PB" to bytes
+func convertStorageToBytes(sizeStr string) (int, error) {
+ const (
+ BYTE = 1
+ KB = 1024 * BYTE
+ MB = 1024 * KB
+ GB = 1024 * MB
+ TB = 1024 * GB
+ PB = 1024 * TB
+ )
+ // Remove any spaces and convert to uppercase
+ sizeStr = strings.TrimSpace(sizeStr)
+ sizeStr = strings.ToUpper(sizeStr)
+
+ // Regular expression to extract number and unit
+ re := regexp.MustCompile(`^(\d+(?:\.\d+)?)\s*(GB|TB|PB)$`)
+ matches := re.FindStringSubmatch(sizeStr)
+
+ if len(matches) != 3 {
+ return 0, fmt.Errorf("invalid size format: %q. Expected format like '20TB', '500GB', '1.5PB'", sizeStr)
+ }
+
+ // Parse the numeric value
+ value, err := strconv.ParseFloat(matches[1], 64)
+ if err != nil {
+ return 0, fmt.Errorf("failed to parse numeric value from %q: %v", sizeStr, err)
+ }
+
+ // Get the unit multiplier
+ unit := matches[2]
+ var multiplier int64
+
+ switch unit {
+ case "GB":
+ multiplier = GB
+ case "TB":
+ multiplier = TB
+ case "PB":
+ multiplier = PB
+ default:
+ return 0, fmt.Errorf("unsupported storage unit: %q. Supported units: GB, TB, PB", unit)
+ }
+
+ // Calculate total bytes
+ totalBytes := int64(value * float64(multiplier))
+
+ return int(totalBytes), nil
+}
+
+func getNtpServersAndTimezones(ctx context.Context, client *http.Client, engine_host string) ([]string, string, error) {
+ ntpURL := engine_host + ENGINE_APIS["NTP_CONFIG"]
+ req, err := http.NewRequest(http.MethodGet, ntpURL, nil)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error creating NTP request: "+err.Error())
+ return nil, "", err
+ }
+ req.Header.Set("Content-Type", "application/json")
+ tflog.Info(ctx, DLPX+INFO+"GET NTP Request URL: "+ntpURL)
+ resp, err := client.Do(req)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error getting NTP servers: "+err.Error())
+ return nil, "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error reading NTP response: "+err.Error())
+ return nil, "", err
+ }
+
+ var ntpRes GetNTPResponse
+ err = json.Unmarshal(body, &ntpRes)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error unmarshaling NTP response: "+err.Error())
+ return nil, "", err
+ }
+ if ntpRes.Status != "OK" {
+ tflog.Error(ctx, DLPX+ERROR+"NTP API returned non-OK status: "+ntpRes.Status)
+ return nil, "", fmt.Errorf("NTP API returned error status: %s", ntpRes.Status)
+ }
+
+ // Extract servers and timezone
+ servers := ntpRes.Result.NtpConfig.Servers
+ timezone := ntpRes.Result.SystemTimeZone
+ return servers, timezone, nil
+}
+
+func getDNSConfiguration(ctx context.Context, client *http.Client, engine_host string) (GetDNSResponse, error) {
+ {
+ dnsURL := engine_host + ENGINE_APIS["DNS_CONFIG"]
+ req, err := http.NewRequest(http.MethodGet, dnsURL, nil)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error creating DNS request: "+err.Error())
+ return GetDNSResponse{}, err
+ }
+ req.Header.Set("Content-Type", "application/json")
+ tflog.Info(ctx, DLPX+INFO+"GET DNS Request URL: "+dnsURL)
+ resp, err := client.Do(req)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error getting DNS configuration: "+err.Error())
+ return GetDNSResponse{}, err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error reading DNS response: "+err.Error())
+ return GetDNSResponse{}, err
+ }
+
+ var dnsRes GetDNSResponse
+ err = json.Unmarshal(body, &dnsRes)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error unmarshaling DNS response: "+err.Error())
+ return GetDNSResponse{}, err
+ }
+ if dnsRes.Status != "OK" {
+ tflog.Error(ctx, DLPX+ERROR+"DNS API returned non-OK status: "+dnsRes.Status)
+ return GetDNSResponse{}, fmt.Errorf("DNS API returned error status: %s", dnsRes.Status)
+ }
+
+ return dnsRes, nil
+ }
+}
+
+func getEntityIDForSSO(ctx context.Context, client *http.Client, engine_host string) (string, error) {
+ ssoURL := engine_host + ENGINE_APIS["SSO_CONFIG"]
+ req, err := http.NewRequest(http.MethodGet, ssoURL, nil)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error creating SSO request: "+err.Error())
+ return "", err
+ }
+ req.Header.Set("Content-Type", "application/json")
+ tflog.Info(ctx, DLPX+INFO+"GET SSO Request URL: "+ssoURL)
+ resp, err := client.Do(req)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error getting SSO details: "+err.Error())
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error reading SSO response: "+err.Error())
+ return "", err
+ }
+ tflog.Info(ctx, DLPX+INFO+"SSO Response Body: "+string(body))
+ var ssoRes map[string]interface{}
+ err = json.Unmarshal(body, &ssoRes)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error unmarshaling SSO response: "+err.Error())
+ return "", err
+ }
+ if bodyMap, ok := ssoRes["result"].(map[string]interface{}); ok {
+ if entityId, exists := bodyMap["entityId"].(string); exists {
+ return entityId, nil
+ }
+ }
+ return "", fmt.Errorf("failed to retrieve EntityID for sso")
+}
diff --git a/internal/provider/engine_api_utility.go b/internal/provider/engine_api_utility.go
index 0ea1e1f..e93b2c1 100644
--- a/internal/provider/engine_api_utility.go
+++ b/internal/provider/engine_api_utility.go
@@ -1,9 +1,14 @@
package provider
import (
+ "bytes"
"context"
"encoding/json"
+ "fmt"
+ "io"
"net/http"
+ "regexp"
+ "strings"
"time"
"github.com/hashicorp/terraform-plugin-log/tflog"
@@ -19,10 +24,12 @@ func pollActionStatus(ctx context.Context, client *http.Client, engine_host stri
if err != nil {
return diag.Errorf("Error getting action: %s", err)
}
+ tflog.Info(ctx, DLPX+INFO+" action data "+string(actionData))
var actionResult ActionResult
err = json.Unmarshal(actionData, &actionResult)
if err != nil {
tflog.Error(ctx, DLPX+ERROR+" Error unmarshalling "+err.Error())
+ return diag.Errorf("Error unmarshalling action result: %s", err)
}
tflog.Info(ctx, DLPX+INFO+" action state "+actionResult.Result.State)
if actionResult.Result.State == "COMPLETED" {
@@ -74,12 +81,12 @@ func UpdateUserPassword(ctx context.Context, client *http.Client, engine_host st
return diags
}
-func initializeSystemAndDevices(ctx context.Context, client *http.Client, engine_host string, user string, default_email string, password string) (ActionResult, diag.Diagnostics) {
+func initializeSystemAndDevices(ctx context.Context, client *http.Client, engine_host string, params InitializationParameters) (APIResponse, diag.Diagnostics) {
var diags diag.Diagnostics
// Get Devices
deviceData, err := getDevices(ctx, client, engine_host)
if err != nil {
- return ActionResult{}, diag.Errorf("Error getting devices: %s", err)
+ return APIResponse{}, diag.Errorf("Error getting devices: %s", err)
}
tflog.Info(ctx, DLPX+INFO+"devices "+string(deviceData))
@@ -87,20 +94,256 @@ func initializeSystemAndDevices(ctx context.Context, client *http.Client, engine
var resultList ListResult
err = json.Unmarshal(deviceData, &resultList)
if err != nil {
- return ActionResult{}, diag.Errorf("Error parsing device information: %s", err)
+ return APIResponse{}, diag.Errorf("Error parsing device information: %s", err)
}
// Initialize System
- resp, err := initializeSystem(ctx, client, engine_host, resultList, user, default_email, password)
+ resp, err := initializeSystem(ctx, client, engine_host, resultList, params)
if err != nil {
- return ActionResult{}, diag.Errorf("Error initializing system: %s", err)
+ return APIResponse{}, diag.Errorf("Error initializing system: %s", err)
}
tflog.Info(ctx, DLPX+INFO+"Initializing system: "+string(resp))
- var result ActionResult
+ var result APIResponse
unmarshalErr := json.Unmarshal(resp, &result)
if unmarshalErr != nil {
tflog.Error(ctx, DLPX+ERROR+"Error unmarshalling: "+unmarshalErr.Error())
+ return APIResponse{}, diag.Errorf("Error initializing system: %s", unmarshalErr)
}
return result, diags
}
+
+func setNtpServers(ctx context.Context, client *http.Client, engine_host string, ntp_servers []string, ntp_timezone string) (APIResponse, error) {
+ tflog.Info(ctx, DLPX+INFO+"Setting NTP Servers")
+ // Get default Timezone from Engine
+ _, timezone, err := getNtpServersAndTimezones(ctx, client, engine_host)
+ if err != nil {
+ return APIResponse{}, nil
+ }
+
+ if ntp_timezone != "" {
+ timezone = ntp_timezone
+ }
+ // Set NTP Servers on Engine
+ ntpPayload := NTPServerParams{
+ Type: "TimeConfig",
+ SystemTimeZone: timezone,
+ NTPConfig: SetNTPConfig{
+ Type: "NTPConfig",
+ Enabled: true,
+ Servers: ntp_servers,
+ },
+ }
+
+ ntpURL := engine_host + ENGINE_APIS["NTP_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, ntpPayload, ntpURL, "NTP")
+ return res, err
+}
+
+func configureSMTP(ctx context.Context, client *http.Client, engine_host string, smtp_config map[string]interface{}) (APIResponse, error) {
+ var config SMTPConfig
+ tflog.Info(ctx, DLPX+INFO+"Configuring SMTP Settings")
+ var isSMTPAuthentication bool
+ if len(smtp_config["smtp_authentication"].([]interface{})) > 0 {
+ isSMTPAuthentication = true
+ }
+ tflog.Info(ctx, DLPX+INFO+"SMTP Config Map: "+fmt.Sprintf("%+v", smtp_config))
+ config = SMTPConfig{
+ Type: "SMTPConfig",
+ Enabled: true,
+ Server: smtp_config["server"].(string),
+ Port: smtp_config["port"].(int),
+ AuthenticationEnabled: isSMTPAuthentication,
+ TlsEnabled: smtp_config["tls_authentication"].(bool),
+ FromAddress: smtp_config["from_email_address"].(string),
+ SendTimeout: smtp_config["send_timeout"].(int),
+ }
+ tflog.Info(ctx, DLPX+INFO+"SMTP Config before adding auth details: "+fmt.Sprintf("%+v", config))
+ if len(smtp_config["smtp_authentication"].([]interface{})) > 0 {
+ config.Username = smtp_config["smtp_authentication"].([]interface{})[0].(map[string]interface{})["user"].(string)
+ config.Password = smtp_config["smtp_authentication"].([]interface{})[0].(map[string]interface{})["password"].(string)
+ }
+ tflog.Info(ctx, DLPX+INFO+"Final SMTP Config Struct: "+fmt.Sprintf("%+v", config))
+ smtpURL := engine_host + ENGINE_APIS["SMTP_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, config, smtpURL, "SMTP")
+ return res, err
+}
+
+func configureDNS(ctx context.Context, client *http.Client, engine_host string, dns_config map[string]interface{}) (APIResponse, error) {
+ existingDnsConfig, err := getDNSConfiguration(ctx, client, engine_host)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error getting existing DNS configuration: "+err.Error())
+ return APIResponse{}, err
+ }
+ tflog.Info(ctx, DLPX+INFO+"Existing DNS Configuration: "+fmt.Sprintf("%+v", existingDnsConfig))
+ var dnsServers []string
+ var domains []string
+
+ if len(existingDnsConfig.Result.Servers) > 0 {
+ dnsServers = existingDnsConfig.Result.Servers
+ }
+ if len(existingDnsConfig.Result.Domains) > 0 {
+ domains = existingDnsConfig.Result.Domains
+ }
+
+ tflog.Info(ctx, DLPX+INFO+"Existing Domains"+fmt.Sprintf("%+v", domains))
+ tflog.Info(ctx, DLPX+INFO+"Existing DNS Servers"+fmt.Sprintf("%+v", dnsServers))
+ dnsURL := engine_host + ENGINE_APIS["DNS_CONFIG"]
+ var dnsPayload DNSConfig
+ if len(toStringArray(dns_config["servers"])) > 0 {
+ dnsServers = append(dnsServers, toStringArray(dns_config["servers"])...)
+ }
+ dnsPayload.Servers = dnsServers
+ if len(toStringArray(dns_config["domains"])) > 0 {
+ domains = append(domains, toStringArray(dns_config["domains"])...)
+ }
+ dnsPayload.Domains = domains
+ dnsPayload.Type = "DNSConfig"
+ tflog.Info(ctx, DLPX+INFO+"Final DNS Config Struct: "+fmt.Sprintf("%+v", dnsPayload))
+ res, err := processRequestAndResponse(ctx, client, dnsPayload, dnsURL, "DNS")
+ return res, err
+}
+
+func configurePhoneHome(ctx context.Context, client *http.Client, engine_host string, enable bool) (APIResponse, error) {
+ tflog.Info(ctx, DLPX+INFO+"Configuring Phone Home Setting to "+fmt.Sprintf("%t", enable))
+ phoneHomePayload := PhoneHomeConfig{
+ Type: "PhoneHomeService",
+ Enabled: enable,
+ }
+ phoneHomeURL := engine_host + ENGINE_APIS["PHONE_HOME_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, phoneHomePayload, phoneHomeURL, "Phone Home")
+ return res, err
+}
+
+func configureUserAnalytics(ctx context.Context, client *http.Client, engine_host string, enable bool) (APIResponse, error) {
+ tflog.Info(ctx, DLPX+INFO+"Configuring User Analytics Setting to "+fmt.Sprintf("%t", enable))
+ userAnalyticsPayload := UserAnalyticsConfig{
+ Type: "UserInterfaceConfig",
+ AnalyticsEnabled: enable,
+ }
+ userAnalyticsURL := engine_host + ENGINE_APIS["USER_ANALYTICS_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, userAnalyticsPayload, userAnalyticsURL, "User Analytics")
+ return res, err
+}
+
+func configureWebProxy(ctx context.Context, client *http.Client, engine_host string, web_proxy_config map[string]interface{}) (APIResponse, error) {
+ tflog.Info(ctx, DLPX+INFO+"Configuring Web Proxy Settings")
+ tflog.Info(ctx, DLPX+INFO+"Web Proxy config before adding auth details: "+fmt.Sprintf("%+v", web_proxy_config))
+ webProxyPayload := WebProxyConfig{
+ Type: "ProxyService",
+ Https: &ProxyConfiguration{
+ Host: web_proxy_config["host"].(string),
+ Port: web_proxy_config["port"].(int),
+ Enabled: true,
+ Type: "ProxyConfiguration",
+ },
+ }
+
+ if val, ok := web_proxy_config["username"]; ok && val.(string) != "" {
+ webProxyPayload.Https.Username = web_proxy_config["username"].(string)
+ }
+ if val, ok := web_proxy_config["password"]; ok && val.(string) != "" {
+ webProxyPayload.Https.Password = web_proxy_config["password"].(string)
+ }
+
+ webProxyURL := engine_host + ENGINE_APIS["WEB_PROXY_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, webProxyPayload, webProxyURL, "Web Proxy")
+ return res, err
+}
+
+func configureSSO(ctx context.Context, client *http.Client, engine_host string, sso_config map[string]interface{}) (APIResponse, error) {
+ tflog.Info(ctx, DLPX+INFO+"Configuring SSO Settings")
+ ssoPayload := SSOConfig{
+ Type: "SsoConfig",
+ Enabled: sso_config["enabled"].(bool),
+ SamlMetadata: sso_config["saml_metadata"].(string),
+ }
+
+ if val, ok := sso_config["response_skew_time"]; ok && val != 0 {
+ ssoPayload.ResponseSkewTime = sso_config["response_skew_time"].(int)
+ } else {
+ ssoPayload.ResponseSkewTime = DEFAULT_SSO_SKEW_TIME
+ }
+
+ if val, ok := sso_config["max_authentication_age"]; ok && val != 0 {
+ ssoPayload.MaxAuthenticationAge = sso_config["max_authentication_age"].(int)
+ } else {
+ ssoPayload.MaxAuthenticationAge = DEFAULT_SSO_MAX_AUTH_AGE
+ }
+
+ entityId, err := getEntityIDForSSO(ctx, client, engine_host)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error getting existing SSO configuration: "+err.Error())
+ return APIResponse{}, err
+ }
+ tflog.Info(ctx, DLPX+INFO+"Existing SSO Configuration: "+fmt.Sprintf("%+v", entityId))
+
+ if entityId != "" {
+ ssoPayload.EntityId = entityId
+ }
+
+ ssoURL := engine_host + ENGINE_APIS["SSO_CONFIG"]
+ res, err := processRequestAndResponse(ctx, client, ssoPayload, ssoURL, "SSO")
+ return res, err
+}
+
+func validateStorageSize(v interface{}, k string) (warnings []string, errors []error) {
+ value := v.(string)
+
+ // Regular expression to match number followed by GB, TB, or PB (case insensitive)
+ pattern := `^\d+(?:\.\d+)?\s*(GB|TB|PB)$`
+ matched, err := regexp.MatchString(pattern, value)
+
+ if err != nil {
+ errors = append(errors, fmt.Errorf("error validating %s: %s", k, err))
+ return
+ }
+
+ if !matched {
+ errors = append(errors, fmt.Errorf("%s must be a valid storage size with units (e.g., '20TB', '500GB', '1.5PB')", k))
+ return
+ }
+
+ return
+}
+
+func processRequestAndResponse(ctx context.Context, client *http.Client, payload interface{}, apiURL string, config_name string) (APIResponse, error) {
+ postData, err := json.Marshal(payload)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error marshalling "+config_name+" configuration: "+err.Error())
+ return APIResponse{}, err
+ }
+
+ req, er := http.NewRequest(http.MethodPost, apiURL, bytes.NewReader(postData))
+ if er != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error creating "+config_name+" request: "+er.Error())
+ return APIResponse{}, er
+ }
+ req.Header.Set("Content-Type", "application/json")
+ resp, err := client.Do(req)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error configuring "+config_name+": "+err.Error())
+ return APIResponse{}, err
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ bodyStr := string(body)
+ if strings.Contains(bodyStr, `"status":"ERROR"`) {
+ tflog.Error(ctx, DLPX+ERROR+"API returned error response: "+bodyStr)
+ return APIResponse{}, fmt.Errorf("%s configuration failed: %s", config_name, bodyStr)
+ }
+
+ tflog.Info(ctx, DLPX+INFO+config_name+" Configuration Response Body: "+string(body))
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error reading "+config_name+" response: "+err.Error())
+ return APIResponse{}, err
+ }
+ var res APIResponse
+ err = json.Unmarshal(body, &res)
+ if err != nil {
+ tflog.Error(ctx, DLPX+ERROR+"error unmarshalling "+config_name+" response: "+err.Error())
+ return APIResponse{}, err
+ }
+ return res, nil
+}
diff --git a/internal/provider/models.go b/internal/provider/models.go
index c35edd4..215c16d 100644
--- a/internal/provider/models.go
+++ b/internal/provider/models.go
@@ -37,12 +37,19 @@ type ListResult struct {
Overflow bool `json:"overflow"`
}
-type SystemInitializationParameters struct {
+type SystemInitializationBlockStorage struct {
Type string `json:"type"`
DefaultUser string `json:"defaultUser"`
DefaultPassword string `json:"defaultPassword"`
- Devices []string `json:"devices"`
- DefaultEmail string `json:"defaultEmail"`
+ DefaultEmail string `json:"defaultEmail,omitempty"`
+ Devices []string `json:"devices,omitempty"`
+}
+type SystemInitializationObjectStore struct {
+ Type string `json:"type"`
+ DefaultUser string `json:"defaultUser"`
+ DefaultPassword string `json:"defaultPassword"`
+ DefaultEmail string `json:"defaultEmail,omitempty"`
+ ObjectStore *ObjectStore `json:"objectStore,omitempty"`
}
// type ActionResult struct {
@@ -100,3 +107,189 @@ type SystemInfo struct {
Type string `json:"type"`
EngineType string `json:"engineType"`
}
+
+type InitializationParameters struct {
+ User string
+ Email string
+ Password string
+ DeviceType string
+ Endpoint string `json:"endpoint,omitempty"`
+ Region string `json:"region,omitempty"`
+ Bucket string `json:"bucket,omitempty"`
+ Size string `json:"size,omitempty"`
+ AuthType string `json:"auth_type,omitempty"`
+ ACCESS_ID string `json:"access_id,omitempty"`
+ ACCESS_KEY string `json:"access_key,omitempty"`
+ S3_INSTANCE_PROFILE string `json:"s3_instance_profile,omitempty"`
+}
+
+type TestConnection struct {
+ Endpoint string `json:"endpoint"`
+ Region string `json:"region"`
+ Bucket string `json:"bucket"`
+ Type string `json:"type"`
+ AccessCredentials ObjectStoreAccessCredentials `json:"accessCredentials"`
+}
+
+type TestConnectionResult struct {
+ Type string `json:"type"`
+ Status string `json:"status"`
+ Result ObjectStoreTestResult `json:"result"`
+ Job interface{} `json:"job"`
+ Action interface{} `json:"action"`
+}
+
+type ObjectStoreAccessCredentials struct {
+ Type string `json:"type"`
+ ACCESS_ID string `json:"accessId,omitempty"`
+ ACCESS_KEY string `json:"accessKey,omitempty"`
+}
+
+type ObjectStore struct {
+ Type string `json:"type"`
+ Size int `json:"size"`
+ CacheDevices []string `json:"cacheDevices"`
+ Endpoint string `json:"endpoint"`
+ Region string `json:"region"`
+ Bucket string `json:"bucket"`
+ AccessCredentials *ObjectStoreAccessCredentials `json:"accessCredentials"`
+}
+
+type ObjectStoreTestResult struct {
+ Type string `json:"type"`
+ Result bool `json:"result"`
+ ErrorMessage string `json:"errorMessage,omitempty"`
+}
+
+type APIResponse struct {
+ Type string `json:"type"`
+ Status string `json:"status"`
+ Action string `json:"action"`
+ Job string `json:"job"`
+ Result string `json:"result"`
+}
+
+type SetNTPConfig struct {
+ Enabled bool `json:"enabled"`
+ Servers []string `json:"servers"`
+ Type string `json:"type"`
+}
+
+type NTPServerParams struct {
+ SystemTimeZone string `json:"systemTimeZone"`
+ NTPConfig SetNTPConfig `json:"ntpConfig"`
+ Type string `json:"type"`
+}
+
+type GetNTPResponse struct {
+ Type string `json:"type"`
+ Status string `json:"status"`
+ Result TimeConfig `json:"result"`
+ Job interface{} `json:"job"`
+ Action interface{} `json:"action"`
+}
+
+type TimeConfig struct {
+ Type string `json:"type"`
+ CurrentTime string `json:"currentTime"`
+ SystemTimeZone string `json:"systemTimeZone"`
+ SystemTimeZoneOffset int `json:"systemTimeZoneOffset"`
+ SystemTimeZoneOffsetString string `json:"systemTimeZoneOffsetString"`
+ NtpConfig NTPConfig `json:"ntpConfig"`
+}
+
+type NTPConfig struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+ Servers []string `json:"servers"`
+ UseMulticast bool `json:"useMulticast"`
+ MulticastAddress string `json:"multicastAddress"`
+}
+
+type SMTPConfig struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+ Server string `json:"server"`
+ Port int `json:"port"`
+ AuthenticationEnabled bool `json:"authenticationEnabled"`
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+ TlsEnabled bool `json:"tlsEnabled"`
+ FromAddress string `json:"fromAddress"`
+ SendTimeout int `json:"sendTimeout"`
+}
+
+type DNSConfig struct {
+ Type string `json:"type"`
+ Servers []string `json:"servers"`
+ Domains []string `json:"domain,omitempty"`
+}
+
+type GetDNSResponse struct {
+ Type string `json:"type"`
+ Status string `json:"status"`
+ Result DNSConfig `json:"result"`
+ Job interface{} `json:"job"`
+ Action interface{} `json:"action"`
+}
+
+type PhoneHomeConfig struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+}
+
+type UserAnalyticsConfig struct {
+ Type string `json:"type"`
+ AnalyticsEnabled bool `json:"analyticsEnabled"`
+}
+
+type WebProxyConfig struct {
+ Type string `json:"type"`
+ Https *ProxyConfiguration `json:"https,omitempty"`
+}
+
+type ProxyConfiguration struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+ Host string `json:"host"`
+ Port int `json:"port"`
+ Username string `json:"username,omitempty"`
+ Password string `json:"password,omitempty"`
+}
+
+type SSOConfig struct {
+ Type string `json:"type"`
+ Enabled bool `json:"enabled"`
+ EntityId string `json:"entityId"`
+ SamlMetadata string `json:"samlMetadata"`
+ ResponseSkewTime int `json:"responseSkewTime"`
+ MaxAuthenticationAge int `json:"maxAuthenticationAge"`
+}
+
+const (
+ BLOCK = "BLOCK"
+ OBJECT = "OBJECT"
+ ROLE = "ROLE"
+ ACCESS_KEY = "ACCESS_KEY"
+ DEFAULT_SSO_SKEW_TIME = 120
+ DEFAULT_SSO_MAX_AUTH_AGE = 86400
+ DEFAULT_SEND_TIMEOUT = 60
+)
+
+var ENGINE_APIS = map[string]string{
+ "SESSION": "/resources/json/delphix/session",
+ "LOGIN": "/resources/json/delphix/login",
+ "STORAGE_DEVICE": "/resources/json/delphix/storage/device",
+ "USER": "/resources/json/delphix/user/",
+ "SYSTEM_INITIALIZATION": "/resources/json/delphix/domain/initializeSystem",
+ "NTP_CONFIG": "/resources/json/delphix/service/time",
+ "SMTP_CONFIG": "/resources/json/delphix/service/smtp",
+ "DNS_CONFIG": "/resources/json/delphix/service/dns",
+ "PHONE_HOME_CONFIG": "/resources/json/delphix/service/phonehome",
+ "USER_ANALYTICS_CONFIG": "/resources/json/delphix/service/userInterface",
+ "WEB_PROXY_CONFIG": "/resources/json/delphix/service/proxy",
+ "SSO_CONFIG": "/resources/json/delphix/service/sso",
+ "OBJECT_STORE_TEST_CONNECTION": "/resources/json/delphix/storage/objectStorage/testConnection",
+ "ACTION": "/resources/json/delphix/action/",
+ "SYSTEM_INFO": "/resources/json/delphix/system",
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index cdad6b5..7c2e3d6 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -59,14 +59,14 @@ func Provider(version string) func() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
- "delphix_vdb": resourceVdb(),
- "delphix_vdb_group": resourceVdbGroup(),
- "delphix_environment": resourceEnvironment(),
- "delphix_appdata_dsource": resourceAppdataDsource(),
- "delphix_oracle_dsource": resourceOracleDsource(),
- "delphix_database_postgresql": resourceSource(),
- "delphix_engine_configuration": resourceEngineConfiguration(),
- "delphix_engine_registration": resourceEngineRegistration(),
+ "delphix_vdb": resourceVdb(),
+ "delphix_vdb_group": resourceVdbGroup(),
+ "delphix_environment": resourceEnvironment(),
+ "delphix_appdata_dsource": resourceAppdataDsource(),
+ "delphix_oracle_dsource": resourceOracleDsource(),
+ "delphix_database_postgresql": resourceSource(),
+ "delphix_engine_configuration": resourceEngineConfiguration(),
+ "delphix_engine_dct_registration": resourceEngineRegistration(),
},
}
diff --git a/internal/provider/resource_engine_configuration.go b/internal/provider/resource_engine_configuration.go
index fb77af9..36513f0 100644
--- a/internal/provider/resource_engine_configuration.go
+++ b/internal/provider/resource_engine_configuration.go
@@ -3,13 +3,18 @@ package provider
import (
"context"
"encoding/json"
+ "errors"
+ "fmt"
"net/http"
"net/http/cookiejar"
+ "os"
+ "regexp"
"time"
"github.com/hashicorp/terraform-plugin-log/tflog"
"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"
)
func resourceEngineConfiguration() *schema.Resource {
@@ -21,6 +26,48 @@ func resourceEngineConfiguration() *schema.Resource {
ReadContext: engineConfigRead,
UpdateContext: engineConfigUpdate,
DeleteContext: engineConfigDelete,
+ CustomizeDiff: func(ctx context.Context, rd *schema.ResourceDiff, i interface{}) error {
+
+ device_type := rd.Get("device_type").(string)
+ if device_type == OBJECT {
+ ospList := rd.Get("object_storage_params").([]interface{})
+ if len(ospList) == 0 {
+ return errors.New("object_storage_params must be provided when device_type is OBJECT")
+ }
+ for _, item := range ospList {
+ if item == nil {
+ continue
+ }
+ block := item.(map[string]interface{})
+
+ authType := block["auth_type"].(string)
+ if authType == ACCESS_KEY && (block["access_id"] == "" || block["access_key"] == "") {
+ return errors.New("access_id and access_key must be provided when auth_type is ACCESS_KEY")
+ }
+
+ }
+ ntp_servers := rd.Get("ntp_servers").([]interface{})
+ ntp_timezone := rd.Get("ntp_timezone").(string)
+ if len(ntp_servers) == 0 || ntp_timezone == "" {
+ return errors.New("ntp_servers and ntp_timezone must be provided when device_type is OBJECT")
+ }
+ }
+
+ smtp_config := rd.Get("smtp_config").([]interface{})
+ if len(smtp_config) > 0 {
+ smtp_block := smtp_config[0].(map[string]interface{})
+ if len(smtp_block["smtp_authentication"].([]interface{})) > 0 {
+ if _, ok := smtp_block["smtp_authentication"].([]interface{})[0].(map[string]interface{})["user"]; !ok {
+ return errors.New("username must be provided in smtp_authentication")
+ }
+ if _, ok := smtp_block["smtp_authentication"].([]interface{})[0].(map[string]interface{})["password"]; !ok {
+ return errors.New("password must be provided in smtp_authentication")
+ }
+ }
+ }
+ return nil
+
+ },
Schema: map[string]*schema.Schema{
"engine_host": {
@@ -41,11 +88,11 @@ func resourceEngineConfiguration() *schema.Resource {
Required: true,
Sensitive: true,
},
- // "sys_new_password": {
- // Type: schema.TypeString,
- // Required: true,
- // Sensitive: true,
- // },
+ "sys_new_password": {
+ Type: schema.TypeString,
+ Required: true,
+ Sensitive: true,
+ },
"user": {
Type: schema.TypeString,
Required: true,
@@ -149,6 +196,199 @@ func resourceEngineConfiguration() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
+ "device_type": {
+ Type: schema.TypeString,
+ Required: true,
+ ValidateFunc: validation.StringInSlice([]string{BLOCK, OBJECT}, false),
+ },
+ "object_storage_params": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "region": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "bucket": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "endpoint": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "size": {
+ Type: schema.TypeString,
+ Required: true,
+ ValidateFunc: validateStorageSize,
+ },
+ "auth_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: ROLE,
+ ValidateFunc: validation.StringInSlice([]string{ROLE, ACCESS_KEY}, false),
+ },
+ "s3_instance_profile": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "S3ObjectStoreAccessInstanceProfile",
+ },
+ "access_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "access_key": {
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ },
+ },
+ },
+ },
+ "ntp_servers": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "ntp_timezone": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "smtp_config": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "server": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "port": {
+ Type: schema.TypeInt,
+ Required: true,
+ },
+ "from_email_address": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "tls_authentication": {
+ Type: schema.TypeBool,
+ Optional: true,
+ },
+ "send_timeout": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: DEFAULT_SEND_TIMEOUT,
+ },
+ "smtp_authentication": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "user": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Required: true,
+ Sensitive: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "dns_config": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "servers": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ ValidateFunc: validation.IsIPAddress, // Optional: validate IP addresses
+ },
+ },
+ "domains": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ ValidateFunc: validation.StringMatch(
+ regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9\-\.]*[a-zA-Z0-9]$`),
+ "must be a valid domain name",
+ ),
+ },
+ },
+ },
+ },
+ },
+ "phone_home_enabled": {
+ Type: schema.TypeBool,
+ Optional: true,
+ },
+ "user_analytics_enabled": {
+ Type: schema.TypeBool,
+ Optional: true,
+ },
+ "web_proxy_config": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "host": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "port": {
+ Type: schema.TypeInt,
+ Required: true,
+ },
+ "username": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Optional: true,
+ Sensitive: true,
+ },
+ },
+ },
+ },
+ "sso_config": {
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "enabled": {
+ Type: schema.TypeBool,
+ Required: true,
+ },
+ "saml_metadata": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "response_skew_time": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: DEFAULT_SSO_SKEW_TIME,
+ },
+ "max_authentication_age": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: DEFAULT_SSO_MAX_AUTH_AGE,
+ },
+ },
+ },
+ },
},
}
}
@@ -165,7 +405,7 @@ func engineConfigCreate(ctx context.Context, d *schema.ResourceData, meta interf
version, _ := d.Get("api_version").(string)
sys_user, _ := d.Get("sys_user").(string)
sys_curr_pass, _ := d.Get("sys_password").(string)
- //sys_new_pass, _ := d.Get("sys_new_password").(string)
+ sys_new_pass, _ := d.Get("sys_new_password").(string)
user, _ := d.Get("user").(string)
email, has_email := d.GetOk("email")
if has_email {
@@ -173,13 +413,27 @@ func engineConfigCreate(ctx context.Context, d *schema.ResourceData, meta interf
}
password := d.Get("password").(string)
engine_type := d.Get("engine_type").(string)
+ device_type := d.Get("device_type").(string)
+ ntp_timezone := d.Get("ntp_timezone").(string)
+ ntp_servers_raw := d.Get("ntp_servers").([]interface{})
+ ntp_servers := make([]string, len(ntp_servers_raw))
- // // Update sys_user password
- // readDiags := UpdateUserPassword(ctx, client, engine_host, version, sys_user, sys_curr_pass, sys_new_pass, "SYSTEM")
- // if readDiags.HasError() {
- // return readDiags
- // }
+ for i, server := range ntp_servers_raw {
+ ntp_servers[i] = server.(string)
+ }
+ smtp_config := d.Get("smtp_config").([]interface{})
+ dns_config := d.Get("dns_config").([]interface{})
+ phonehome := d.Get("phone_home_enabled").(bool)
+ useranalytics := d.Get("user_analytics_enabled").(bool)
+ web_proxy_config := d.Get("web_proxy_config").([]interface{})
+ sso_config := d.Get("sso_config").([]interface{})
+
+ // Update sys_user password
+ readDiag := UpdateUserPassword(ctx, client, engine_host, version, sys_user, sys_curr_pass, sys_new_pass, "SYSTEM")
+ if readDiag.HasError() {
+ return readDiag
+ }
// Start a session
tflog.Info(ctx, DLPX+INFO+"start Session for "+engine_host)
err := startSession(ctx, client, engine_host, version)
@@ -189,13 +443,107 @@ func engineConfigCreate(ctx context.Context, d *schema.ResourceData, meta interf
// Authenticate/login
tflog.Info(ctx, DLPX+INFO+"login as "+sys_user)
- err = login(ctx, client, engine_host, sys_user, sys_curr_pass, "SYSTEM")
+ err = login(ctx, client, engine_host, sys_user, sys_new_pass, "SYSTEM")
if err != nil {
return diag.Errorf("Error logging in: %s", err)
}
+ params := InitializationParameters{
+ User: user,
+ Password: password,
+ Email: default_email,
+ DeviceType: device_type,
+ }
+ if device_type == OBJECT {
+ object_storage_params := d.Get("object_storage_params").([]interface{})
+ params.Size = object_storage_params[0].(map[string]interface{})["size"].(string)
+ params.Endpoint = object_storage_params[0].(map[string]interface{})["endpoint"].(string)
+ params.Region = object_storage_params[0].(map[string]interface{})["region"].(string)
+ params.Bucket = object_storage_params[0].(map[string]interface{})["bucket"].(string)
+ params.AuthType = object_storage_params[0].(map[string]interface{})["auth_type"].(string)
+
+ if params.AuthType == ACCESS_KEY {
+ params.ACCESS_ID = object_storage_params[0].(map[string]interface{})["access_id"].(string)
+ params.ACCESS_KEY = object_storage_params[0].(map[string]interface{})["access_key"].(string)
+ } else {
+ params.S3_INSTANCE_PROFILE = object_storage_params[0].(map[string]interface{})["s3_instance_profile"].(string)
+ }
+ }
+
+ //Set NTP servers
+ if len(ntp_servers) > 0 {
+ _, ntpError := setNtpServers(ctx, client, engine_host, ntp_servers, ntp_timezone)
+ if ntpError != nil {
+ return diag.Errorf("Error setting NTP servers: %s", ntpError)
+ }
+ // readDiag := pollActionStatus(ctx, client, engine_host, ntpRes.Action)
+ // if readDiag.HasError() {
+ // return readDiag
+ // }
+ }
+
+ //Set SMTP Config
+ if len(smtp_config) > 0 {
+ tflog.Info(ctx, DLPX+INFO+"Configuring SMTP settings")
+ smtp_block := smtp_config[0].(map[string]interface{})
+ _, smtpErr := configureSMTP(ctx, client, engine_host, smtp_block)
+ if smtpErr != nil {
+ return diag.Errorf("Error configuring SMTP: %s", smtpErr)
+ }
+ // readDiag := pollActionStatus(ctx, client, engine_host, smtpRes.Action)
+ // if readDiag.HasError() {
+ // return readDiag
+ // }
+ }
+
+ // Configure DNS
+ if len(dns_config) > 0 {
+ tflog.Info(ctx, DLPX+INFO+"Configuring DNS settings")
+ dns_block := dns_config[0].(map[string]interface{})
+ _, dnsErr := configureDNS(ctx, client, engine_host, dns_block)
+ if dnsErr != nil {
+ return diag.Errorf("Error configuring DNS: %s", dnsErr)
+ }
+ // readDiag := pollActionStatus(ctx, client, engine_host, dnsRes.Action)
+ // if readDiag.HasError() {
+ // return readDiag
+ // }
+ }
+
+ // Configure Phone Home
+ if phonehome {
+ tflog.Info(ctx, DLPX+INFO+"Enabling Phone Home")
+ _, phErr := configurePhoneHome(ctx, client, engine_host, phonehome)
+ if phErr != nil {
+ return diag.Errorf("Error configuring Phone Home: %s", phErr)
+ }
+ }
+ // Configure User Analytics
+ if useranalytics {
+ tflog.Info(ctx, DLPX+INFO+"Enabling User Analytics")
+ _, uaErr := configureUserAnalytics(ctx, client, engine_host, useranalytics)
+ if uaErr != nil {
+ return diag.Errorf("Error configuring User Analytics: %s", uaErr)
+ }
+ }
+
+ // Configure Web Proxy
+ if len(web_proxy_config) > 0 {
+ tflog.Info(ctx, DLPX+INFO+"Configuring Web Proxy settings")
+ web_proxy_block := web_proxy_config[0].(map[string]interface{})
+ _, wpErr := configureWebProxy(ctx, client, engine_host, web_proxy_block)
+ if wpErr != nil {
+ return diag.Errorf("Error configuring Web Proxy: %s", wpErr)
+ }
+ // readDiag := pollActionStatus(ctx, client, engine_host, wpRes.Action)
+ // if readDiag.HasError() {
+ // return readDiag
+ // }
+ }
+
// Initialize Engine
- result, readDiags := initializeSystemAndDevices(ctx, client, engine_host, user, default_email, password)
+ result, readDiags := initializeSystemAndDevices(ctx, client, engine_host, params)
+ tflog.Info(ctx, DLPX+INFO+"Initialization action result: "+fmt.Sprintf("%+v", readDiags))
if readDiags.HasError() {
return readDiags
}
@@ -218,7 +566,7 @@ func engineConfigCreate(ctx context.Context, d *schema.ResourceData, meta interf
// Authenticate/login
tflog.Info(ctx, DLPX+INFO+"login as "+sys_user)
- err = login(ctx, client, engine_host, sys_user, sys_curr_pass, "SYSTEM")
+ err = login(ctx, client, engine_host, sys_user, sys_new_pass, "SYSTEM")
if err != nil {
return diag.Errorf("Error logging in: %s", err)
}
@@ -230,6 +578,20 @@ func engineConfigCreate(ctx context.Context, d *schema.ResourceData, meta interf
tflog.Info(ctx, DLPX+INFO+"engine type resp "+string(resp))
+ // Configure SSO
+ if len(sso_config) > 0 {
+ tflog.Info(ctx, DLPX+INFO+"Configuring SSO settings")
+ sso_block := sso_config[0].(map[string]interface{})
+ _, ssoErr := configureSSO(ctx, client, engine_host, sso_block)
+ if ssoErr != nil {
+ return diag.Errorf("Error configuring SSO: %s", ssoErr)
+ }
+ // readDiag := pollActionStatus(ctx, client, engine_host, ssoRes.Action)
+ // if readDiag.HasError() {
+ // return readDiag
+ // }
+ }
+
//Update defaultUser password
readDiags = UpdateUserPassword(ctx, client, engine_host, version, user, password, password, "DOMAIN")
if readDiags.HasError() {
@@ -262,7 +624,7 @@ func engineConfigUpdate(ctx context.Context, d *schema.ResourceData, meta interf
d.Set(key, old)
}
- return diag.Errorf("Action update not available for engine config : dSource")
+ return diag.Errorf("Action update not available for engine config")
}
func engineConfigRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
@@ -278,28 +640,24 @@ func engineConfigRead(ctx context.Context, d *schema.ResourceData, meta interfac
// Start a session
err := startSession(ctx, client, engineId, version)
if err != nil {
- diag.Errorf("Error starting session: %v", err)
+ return diag.Errorf("Error starting session: %v", err)
}
// Authenticate/login
- err = login(ctx, client, engineId, d.Get("sys_user").(string), d.Get("sys_password").(string), "SYSTEM")
+ err = login(ctx, client, engineId, d.Get("sys_user").(string), d.Get("sys_new_password").(string), "SYSTEM")
if err != nil {
- diag.Errorf("Error logging in: %v", err)
+ return diag.Errorf("Error logging in: %v", err)
}
body, err := getSystem(ctx, client, engineId)
if err != nil {
- diag.Errorf("Error getting system info: %v", err)
+ return diag.Errorf("Error getting system info: %v", err)
}
var response SystemInfoResponse
sysErr := json.Unmarshal(body, &response)
- // print("!!!!!!!!!!!!!!!OUTPUT RESPONSE")
- // for k, v := range response.Result {
- // tflog.Debug(ctx, DLPX+INFO+"result field", map[string]interface{}{"key": k, "value": v})
- // }
if sysErr != nil {
- tflog.Error(ctx, DLPX+ERROR+"Error unmarshalling", map[string]interface{}{"error": sysErr.Error()})
+ return diag.Errorf("Error unmarshalling system info response: %v", sysErr)
}
d.Set("configured", response.Result["configured"])
@@ -327,6 +685,12 @@ func engineConfigRead(ctx context.Context, d *schema.ResourceData, meta interfac
}
func engineConfigDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ if os.Getenv("TF_ACC") == "1" {
+ // Terraform acceptance test mode (destroy MUST succeed)
+ d.SetId("")
+ return nil
+ }
+
// get the changed keys
changedKeys := make([]string, 0, len(d.State().Attributes))
for k := range d.State().Attributes {
@@ -339,5 +703,5 @@ func engineConfigDelete(ctx context.Context, d *schema.ResourceData, meta interf
old, _ := d.GetChange(key)
d.Set(key, old)
}
- return diag.Errorf("Action delete not available for engine config : dSource")
+ return diag.Errorf("Action delete not available for engine config")
}
diff --git a/internal/provider/resource_engine_configuration_test.go b/internal/provider/resource_engine_configuration_test.go
new file mode 100644
index 0000000..b9ecbae
--- /dev/null
+++ b/internal/provider/resource_engine_configuration_test.go
@@ -0,0 +1,462 @@
+package provider
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+)
+
+func TestAccEngineConfiguration_blockDevice(t *testing.T) {
+ resourceName := "delphix_engine_configuration.test"
+ engineHost := os.Getenv("DELPHIX_ENGINE_HOST")
+
+ if engineHost == "" {
+ t.Skip("DELPHIX_ENGINE_HOST environment variable not set")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineConfigurationBlockDevice(engineHost),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineConfigurationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "engine_host", engineHost),
+ resource.TestCheckResourceAttr(resourceName, "api_version", "1.11.46"),
+ resource.TestCheckResourceAttr(resourceName, "sys_user", "sysadmin"),
+ resource.TestCheckResourceAttr(resourceName, "user", "admin"),
+ resource.TestCheckResourceAttr(resourceName, "engine_type", "CD"),
+ resource.TestCheckResourceAttr(resourceName, "device_type", "BLOCK"),
+ resource.TestCheckResourceAttrSet(resourceName, "configured"),
+ resource.TestCheckResourceAttrSet(resourceName, "hostname"),
+ resource.TestCheckResourceAttrSet(resourceName, "product_type"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccEngineConfiguration_objectStorageWithRole(t *testing.T) {
+ resourceName := "delphix_engine_configuration.test"
+ engineHost := os.Getenv("DELPHIX_ENGINE_HOST")
+ bucketName := os.Getenv("S3_BUCKET_NAME")
+
+ if engineHost == "" || bucketName == "" {
+ t.Skip("DELPHIX_ENGINE_HOST or S3_BUCKET_NAME environment variable not set")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineConfigurationObjectStorageRole(engineHost, bucketName),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineConfigurationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "device_type", "OBJECT"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.region", "us-west-2"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.bucket", bucketName),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.endpoint", "s3.us-west-2.amazonaws.com"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.size", "20GB"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.auth_type", "ROLE"),
+ resource.TestCheckResourceAttr(resourceName, "ntp_servers.0", "pool.ntp.org"),
+ resource.TestCheckResourceAttr(resourceName, "ntp_servers.1", "time.nist.gov"),
+ resource.TestCheckResourceAttr(resourceName, "ntp_timezone", "America/New_York"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccEngineConfiguration_objectStorageWithAccessKey(t *testing.T) {
+ resourceName := "delphix_engine_configuration.test"
+ engineHost := os.Getenv("DELPHIX_ENGINE_HOST")
+ bucketName := os.Getenv("S3_BUCKET_NAME")
+ accessId := os.Getenv("AWS_ACCESS_KEY_ID")
+ accessKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
+
+ if engineHost == "" || bucketName == "" || accessId == "" || accessKey == "" {
+ t.Skip("Required environment variables not set: DELPHIX_ENGINE_HOST, S3_BUCKET_NAME, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineConfigurationObjectStorageAccessKey(engineHost, bucketName, accessId, accessKey),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineConfigurationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "device_type", "OBJECT"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.auth_type", "ACCESS_KEY"),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.access_id", accessId),
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.access_key", accessKey),
+ resource.TestCheckResourceAttr(resourceName, "ntp_servers.#", "3"),
+ resource.TestCheckResourceAttr(resourceName, "ntp_timezone", "UTC"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccEngineConfiguration_validationErrors(t *testing.T) {
+ engineHost := "http://test-engine.example.com"
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineConfigurationObjectStorageMissingParams(engineHost),
+ ExpectError: regexp.MustCompile("object_storage_params must be provided when device_type is OBJECT"),
+ },
+ {
+ Config: testAccEngineConfigurationObjectStorageMissingAccessKey(engineHost),
+ ExpectError: regexp.MustCompile("access_id and access_key must be provided when auth_type is ACCESS_KEY"),
+ },
+ {
+ Config: testAccEngineConfigurationObjectStorageMissingNTP(engineHost),
+ ExpectError: regexp.MustCompile("ntp_servers and ntp_timezone must be provided when device_type is OBJECT"),
+ },
+ {
+ Config: testAccEngineConfigurationInvalidStorageSize(engineHost),
+ ExpectError: regexp.MustCompile("must be a valid storage size with units"),
+ },
+ },
+ })
+}
+
+func TestValidateStorageSize(t *testing.T) {
+ validSizes := []string{
+ "100GB",
+ "1.5TB",
+ "20TB",
+ "0.5PB",
+ "1000GB",
+ "2.5TB",
+ }
+
+ invalidSizes := []string{
+ "100",
+ "100MB",
+ "100KB",
+ "abc",
+ "100 GB",
+ "100gb",
+ "1.5.5TB",
+ "TB",
+ "",
+ }
+
+ for _, size := range validSizes {
+ warnings, errors := validateStorageSize(size, "size")
+ if len(errors) > 0 {
+ t.Errorf("Expected %s to be valid, got errors: %v", size, errors)
+ }
+ if len(warnings) > 0 {
+ t.Errorf("Expected %s to have no warnings, got: %v", size, warnings)
+ }
+ }
+
+ for _, size := range invalidSizes {
+ _, errors := validateStorageSize(size, "size")
+ if len(errors) == 0 {
+ t.Errorf("Expected %s to be invalid, but got no errors", size)
+ }
+ }
+}
+
+func TestAccEngineConfiguration_comprehensive(t *testing.T) {
+ resourceName := "delphix_engine_configuration.test"
+ engineHost := os.Getenv("DELPHIX_ENGINE_HOST")
+ bucketName := os.Getenv("S3_BUCKET_NAME")
+
+ if engineHost == "" || bucketName == "" {
+ t.Skip("DELPHIX_ENGINE_HOST or S3_BUCKET_NAME environment variable not set")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineConfigurationComprehensive(engineHost, bucketName),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineConfigurationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "device_type", "OBJECT"),
+ // DNS Config
+ resource.TestCheckResourceAttr(resourceName, "dns_config.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "dns_config.0.servers.#", "3"),
+ // NTP Config
+ resource.TestCheckResourceAttr(resourceName, "ntp_servers.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "ntp_timezone", "America/New_York"),
+ // SMTP Config
+ resource.TestCheckResourceAttr(resourceName, "smtp_config.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "smtp_config.0.server", "smtp.example.com"),
+ // Web Proxy Config
+ resource.TestCheckResourceAttr(resourceName, "web_proxy_config.#", "1"),
+ resource.TestCheckResourceAttr(resourceName, "web_proxy_config.0.host", "proxy.internal.com"),
+ // Analytics and Phone Home
+ resource.TestCheckResourceAttr(resourceName, "phone_home_enabled", "true"),
+ resource.TestCheckResourceAttr(resourceName, "user_analytics_enabled", "true"),
+ // Object Storage
+ resource.TestCheckResourceAttr(resourceName, "object_storage_params.0.auth_type", "ROLE"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccEngineConfigurationComprehensive(engineHost, bucketName string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ sys_new_password = "delphix"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ # Object Storage Configuration
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "%s"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "30GB"
+ auth_type = "ROLE"
+ }
+
+ # NTP Configuration
+ ntp_servers = ["pool.ntp.org", "time.nist.gov"]
+ ntp_timezone = "America/New_York"
+
+ # DNS Configuration
+ dns_config {
+ servers = ["172.16.105.22", "172.16.105.23", "8.8.8.8"]
+ domains = ["example.com", "internal.local", "test.local"]
+ }
+
+ # SMTP Configuration
+ smtp_config {
+ server = "smtp.example.com"
+ port = 587
+ from_email_address = "noreply@example.com"
+ tls_authentication = true
+ send_timeout = 120
+
+ smtp_authentication {
+ user = "smtp_user@example.com"
+ password = "smtp_password"
+ }
+ }
+
+ # Web Proxy Configuration
+ web_proxy_config {
+ host = "proxy.internal.com"
+ port = 3128
+ username = "proxy_admin"
+ password = "proxy_secret"
+ }
+
+ # Analytics and Phone Home
+ phone_home_enabled = true
+ user_analytics_enabled = true
+}
+`, engineHost, bucketName)
+}
+
+func testAccCheckEngineConfigurationExists(resourceName string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[resourceName]
+ if !ok {
+ return fmt.Errorf("Resource not found: %s", resourceName)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("No ID is set")
+ }
+
+ // In a real scenario, you might make an API call here to verify the resource exists
+ return nil
+ }
+}
+
+// Test configuration templates
+func testAccEngineConfigurationBlockDevice(engineHost string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "BLOCK"
+}
+`, engineHost)
+}
+
+func testAccEngineConfigurationObjectStorageRole(engineHost, bucketName string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ ntp_servers = ["pool.ntp.org", "time.nist.gov"]
+ ntp_timezone = "America/New_York"
+
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "%s"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "20GB"
+ auth_type = "ROLE"
+ }
+}
+`, engineHost, bucketName)
+}
+
+func testAccEngineConfigurationObjectStorageAccessKey(engineHost, bucketName, accessId, accessKey string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ ntp_servers = ["pool.ntp.org", "time.nist.gov", "1.ubuntu.pool.ntp.org"]
+ ntp_timezone = "UTC"
+
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "%s"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "20GB"
+ auth_type = "ACCESS_KEY"
+ access_id = "%s"
+ access_key = "%s"
+ }
+}
+`, engineHost, bucketName, accessId, accessKey)
+}
+
+func testAccEngineConfigurationObjectStorageMissingParams(engineHost string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+ # Missing object_storage_params
+}
+`, engineHost)
+}
+
+func testAccEngineConfigurationObjectStorageMissingAccessKey(engineHost string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ ntp_servers = ["pool.ntp.org"]
+ ntp_timezone = "UTC"
+
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "test-bucket"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "20GB"
+ auth_type = "ACCESS_KEY"
+ # Missing access_id and access_key
+ }
+}
+`, engineHost)
+}
+
+func testAccEngineConfigurationObjectStorageMissingNTP(engineHost string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ # Missing ntp_servers and ntp_timezone
+
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "test-bucket"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "20GB"
+ auth_type = "ROLE"
+ }
+}
+`, engineHost)
+}
+
+func testAccEngineConfigurationInvalidStorageSize(engineHost string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_configuration" "test" {
+ engine_host = "%s"
+ api_version = "1.11.46"
+ sys_user = "sysadmin"
+ sys_password = "sysadmin"
+ user = "admin"
+ password = "delphix"
+ email = "test@example.com"
+ engine_type = "CD"
+ device_type = "OBJECT"
+
+ ntp_servers = ["pool.ntp.org"]
+ ntp_timezone = "UTC"
+
+ object_storage_params {
+ region = "us-west-2"
+ bucket = "test-bucket"
+ endpoint = "s3.us-west-2.amazonaws.com"
+ size = "20MB" # Invalid size unit
+ auth_type = "ROLE"
+ }
+}
+`, engineHost)
+}
diff --git a/internal/provider/resource_engine_registration.go b/internal/provider/resource_engine_registration.go
index 7827e8e..03dbd85 100644
--- a/internal/provider/resource_engine_registration.go
+++ b/internal/provider/resource_engine_registration.go
@@ -35,8 +35,9 @@ func resourceEngineRegistration() *schema.Resource {
Required: true,
},
"password": {
- Type: schema.TypeString,
- Required: true,
+ Type: schema.TypeString,
+ Required: true,
+ Sensitive: true,
},
"masking_username": {
Type: schema.TypeString,
@@ -297,17 +298,6 @@ func resourceEngineRegistrationDelete(ctx context.Context, d *schema.ResourceDat
return diags
}
- // job_status, job_err := PollJobStatus(*apiRes.Job.Id, ctx, client)
- // if job_err != "" {
- // tflog.Error(ctx, DLPX+ERROR+"Job Polling failed but continuing with engine removal. Error: "+job_err)
- // }
- // if isJobTerminalFailure(job_status) {
- // return diag.Errorf("[NOT OK] Engine-Delete %s. JobId: %s / Error: %s", job_status, *apiRes.Job.Id, job_err)
- // }
- // _, diags := PollForObjectDeletion(ctx, func() (interface{}, *http.Response, error) {
- // return client.ManagementApi.GetRegisteredEngine(ctx, engineID).Execute()
- // })
-
_, diags := PollForObjectExistence(ctx, func() (interface{}, *http.Response, error) {
return client.ManagementAPI.GetRegisteredEngine(ctx, engineID).Execute()
})
diff --git a/internal/provider/resource_engine_registration_test.go b/internal/provider/resource_engine_registration_test.go
new file mode 100644
index 0000000..46252b9
--- /dev/null
+++ b/internal/provider/resource_engine_registration_test.go
@@ -0,0 +1,226 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "math/rand"
+ "os"
+ "regexp"
+ "testing"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+)
+
+func TestAccEngineRegistration_withSSLConfig(t *testing.T) {
+ resourceName := "delphix_engine_dct_registration.test"
+ engineName := "test-engine-ssl-" + randomString(8)
+ engineHostname := os.Getenv("TEST_ENGINE_HOSTNAME")
+ engineUsername := os.Getenv("TEST_ENGINE_USERNAME")
+ enginePassword := os.Getenv("TEST_ENGINE_PASSWORD")
+
+ if engineHostname == "" || engineUsername == "" || enginePassword == "" {
+ t.Skip("Required environment variables not set")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckEngineRegistrationDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineRegistrationWithSSL(
+ engineName, engineHostname, engineUsername, enginePassword,
+ ),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineRegistrationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "name", engineName),
+ resource.TestCheckResourceAttr(resourceName, "insecure_ssl", "true"),
+ resource.TestCheckResourceAttr(resourceName, "unsafe_ssl_hostname_check", "true"),
+ resource.TestCheckResourceAttrSet(resourceName, "id"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccEngineRegistration_withTags(t *testing.T) {
+ resourceName := "delphix_engine_dct_registration.test"
+ engineName := "test-engine-tags-" + randomString(8)
+ engineHostname := os.Getenv("TEST_ENGINE_HOSTNAME")
+ engineUsername := os.Getenv("TEST_ENGINE_USERNAME")
+ enginePassword := os.Getenv("TEST_ENGINE_PASSWORD")
+
+ if engineHostname == "" || engineUsername == "" || enginePassword == "" {
+ t.Skip("Required environment variables not set")
+ }
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ CheckDestroy: testAccCheckEngineRegistrationDestroy,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineRegistrationWithTags(
+ engineName, engineHostname, engineUsername, enginePassword,
+ ),
+ Check: resource.ComposeTestCheckFunc(
+ testAccCheckEngineRegistrationExists(resourceName),
+ resource.TestCheckResourceAttr(resourceName, "name", engineName),
+ resource.TestCheckResourceAttr(resourceName, "tags.#", "2"),
+ resource.TestCheckResourceAttr(resourceName, "tags.0.key", "environment"),
+ resource.TestCheckResourceAttr(resourceName, "tags.0.value", "test"),
+ resource.TestCheckResourceAttr(resourceName, "tags.1.key", "team"),
+ resource.TestCheckResourceAttr(resourceName, "tags.1.value", "qa"),
+ resource.TestCheckResourceAttrSet(resourceName, "id"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccEngineRegistration_validationErrors(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() { testAccPreCheck(t) },
+ Providers: testAccProviders,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccEngineRegistrationMissingName(),
+ ExpectError: regexp.MustCompile("The argument \"name\" is required"),
+ },
+ {
+ Config: testAccEngineRegistrationMissingHostname(),
+ ExpectError: regexp.MustCompile("The argument \"hostname\" is required"),
+ },
+ {
+ Config: testAccEngineRegistrationMissingCredentials(),
+ ExpectError: regexp.MustCompile("The argument \"username\" is required"),
+ },
+ },
+ })
+}
+
+func testAccCheckEngineRegistrationExists(resourceName string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[resourceName]
+ if !ok {
+ return fmt.Errorf("Resource not found: %s", resourceName)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("No ID is set")
+ }
+
+ client := testAccProvider.Meta().(*apiClient).client
+ _, _, err := client.ManagementAPI.GetRegisteredEngine(context.Background(), rs.Primary.ID).Execute()
+ if err != nil {
+ return fmt.Errorf("Error getting registered engine: %s", err)
+ }
+
+ return nil
+ }
+}
+
+func testAccCheckEngineRegistrationDestroy(s *terraform.State) error {
+ client := testAccProvider.Meta().(*apiClient).client
+ time.Sleep(time.Duration(60) * time.Second)
+ for _, rs := range s.RootModule().Resources {
+ if rs.Type != "delphix_engine_dct_registration" {
+ continue
+ }
+
+ _, httpRes, err := client.ManagementAPI.GetRegisteredEngine(context.Background(), rs.Primary.ID).Execute()
+ if err == nil {
+ return fmt.Errorf("Engine registration still exists")
+ }
+
+ // Check if it's a 404 - which means the resource was successfully deleted
+ if httpRes != nil && httpRes.StatusCode == 404 {
+ continue
+ }
+
+ // If we get any other error, return it
+ return fmt.Errorf("Unexpected error checking for deleted engine registration: %s", err)
+ }
+
+ return nil
+}
+
+// Test configuration templates
+
+func testAccEngineRegistrationWithSSL(name, hostname, username, password string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_dct_registration" "test" {
+ name = "%s"
+ hostname = "%s"
+ username = "%s"
+ password = "%s"
+ insecure_ssl = true
+ unsafe_ssl_hostname_check = true
+}
+`, name, hostname, username, password)
+}
+
+// Validation error test configurations
+
+func testAccEngineRegistrationMissingName() string {
+ return `
+resource "delphix_engine_dct_registration" "test" {
+ hostname = "test.example.com"
+ username = "admin"
+ password = "password"
+}
+`
+}
+
+func testAccEngineRegistrationMissingHostname() string {
+ return `
+resource "delphix_engine_dct_registration" "test" {
+ name = "test-engine"
+ username = "admin"
+ password = "password"
+}
+`
+}
+
+func testAccEngineRegistrationMissingCredentials() string {
+ return `
+resource "delphix_engine_dct_registration" "test" {
+ name = "test-engine"
+ hostname = "test.example.com"
+ password = "password"
+}
+`
+}
+
+// Utility function to generate random strings for unique resource names
+func randomString(length int) string {
+ const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
+ result := make([]byte, length)
+ for i := range result {
+ result[i] = charset[rand.Intn(len(charset))]
+ }
+ return string(result)
+}
+
+func testAccEngineRegistrationWithTags(name, hostname, username, password string) string {
+ return fmt.Sprintf(`
+resource "delphix_engine_dct_registration" "test" {
+ name = "%s"
+ hostname = "%s"
+ username = "%s"
+ password = "%s"
+ insecure_ssl = true
+ tags {
+ key = "environment"
+ value = "test"
+ }
+
+ tags {
+ key = "team"
+ value = "qa"
+ }
+}
+`, name, hostname, username, password)
+}
diff --git a/internal/provider/resource_engine_user_management.go b/internal/provider/resource_engine_user_management.go
deleted file mode 100644
index 35b09d2..0000000
--- a/internal/provider/resource_engine_user_management.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package provider
-
-import (
- "context"
- "encoding/json"
- "net/http"
- "net/http/cookiejar"
-
- "github.com/hashicorp/terraform-plugin-log/tflog"
- "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-)
-
-func resourceEngineUserManagement() *schema.Resource {
- return &schema.Resource{
- // This description is used by the documentation generator and the language server.
- Description: "Resource for Engine User Management.",
-
- CreateContext: engineUserCreate,
- ReadContext: engineUserRead,
- UpdateContext: engineUserUpdate,
- DeleteContext: engineUserDelete,
-
- Schema: map[string]*schema.Schema{
- "engine_host": {
- Type: schema.TypeString,
- Required: true,
- },
- "version": {
- Type: schema.TypeString,
- Required: true,
- },
- "login_user": {
- Type: schema.TypeString,
- Required: true,
- Sensitive: true,
- },
- "login_password": {
- Type: schema.TypeString,
- Required: true,
- Sensitive: true,
- },
- "user_name": {
- Type: schema.TypeString,
- Required: true,
- Sensitive: true,
- },
- "password": {
- Type: schema.TypeString,
- Required: true,
- Sensitive: true,
- },
- "first_name": {
- Type: schema.TypeString,
- Optional: true,
- },
- "last_name": {
- Type: schema.TypeString,
- Optional: true,
- },
- "email": {
- Type: schema.TypeString,
- Optional: true,
- },
- "user_type": {
- Type: schema.TypeString,
- Optional: true,
- },
- },
- } // maye be add enabled and other phone no feilds
-}
-
-func engineUserCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- var diags diag.Diagnostics
-
- // Create a cookie jar to store session cookies
- jar, _ := cookiejar.New(nil)
- client := &http.Client{Jar: jar}
-
- engine_host, _ := d.Get("engine_host").(string)
- version, _ := d.Get("version").(string)
- login_user, _ := d.Get("login_user").(string)
- login_password, _ := d.Get("login_password").(string)
- user_name, _ := d.Get("user_name").(string)
- password, _ := d.Get("password").(string)
- user_type, _ := d.Get("user_type").(string)
-
- // Start a session
- tflog.Info(ctx, DLPX+INFO+"start Session for "+engine_host)
- err := startSession(ctx, client, engine_host, version)
- if err != nil {
- return diag.Errorf("Error starting session: %s", err)
- }
-
- // Authenticate/login
- tflog.Info(ctx, DLPX+INFO+"login as "+login_user)
- err = login(ctx, client, engine_host, login_user, login_password, user_type)
- if err != nil {
- return diag.Errorf("Error logging in: %s", err)
- }
-
- //Add check to see if it is already existing user
-
- action, err := createOrUpdateUser(ctx, client, engine_host, user_name, password, user_type)
- if err != nil {
- return diag.Errorf("Error logging in: %s", err)
- }
-
- var result ActionResult
- unmarshalErr := json.Unmarshal(action, &result)
- if unmarshalErr != nil {
- tflog.Error(ctx, DLPX+ERROR+"Error unmarshalling: "+unmarshalErr.Error())
- }
-
- tflog.Info(ctx, DLPX+INFO+"User Create Successfull!")
-
- d.SetId(engine_host)
-
- readDiags := engineUserRead(ctx, d, meta)
- if readDiags.HasError() {
- return readDiags
- }
-
- return diags
-}
-
-func engineUserUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- // get the changed keys
- changedKeys := make([]string, 0, len(d.State().Attributes))
- for k := range d.State().Attributes {
- if d.HasChange(k) {
- changedKeys = append(changedKeys, k)
- }
- }
- // revert and set the old value to the changed keys
- for _, key := range changedKeys {
- old, _ := d.GetChange(key)
- d.Set(key, old)
- }
-
- return diag.Errorf("Action update not available for engine config : dSource")
-}
-
-func engineUserRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- var diags diag.Diagnostics
-
- engineId := d.Id()
- version, _ := d.Get("version").(string)
-
- // Create a cookie jar to store session cookies
- jar, _ := cookiejar.New(nil)
- client := &http.Client{Jar: jar}
-
- // Start a session
- err := startSession(ctx, client, engineId, version)
- if err != nil {
- diag.Errorf("Error starting session: %v", err)
- }
-
- // Authenticate/login
- err = login(ctx, client, engineId, d.Get("sys_user").(string), d.Get("sys_new_password").(string), "SYSTEM")
- if err != nil {
- diag.Errorf("Error logging in: %v", err)
- }
-
- body, err := getSystem(ctx, client, engineId)
- if err != nil {
- diag.Errorf("Error getting system info: %v", err)
- }
-
- var response SystemInfoResponse
- sysErr := json.Unmarshal(body, &response)
- if sysErr != nil {
- tflog.Error(ctx, DLPX+ERROR+"Error unmarshalling", map[string]interface{}{"error": sysErr.Error()})
- }
-
- d.Set("configured", response.Result["configured"])
- d.Set("hostname", response.Result["hostname"])
-
- return diags
-}
-
-func engineUserDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- // get the changed keys
- changedKeys := make([]string, 0, len(d.State().Attributes))
- for k := range d.State().Attributes {
- if d.HasChange(k) {
- changedKeys = append(changedKeys, k)
- }
- }
- // revert and set the old value to the changed keys
- for _, key := range changedKeys {
- old, _ := d.GetChange(key)
- d.Set(key, old)
- }
-
- return diag.Errorf("Action delete not available for engine config : dSource")
-}