Repository: rancher/terraform-provider-rancher2

Issue:
fix: [BACKPORT][v8] Add Oracle Kubernetes Engine back to terraform
tracking the backport of #1538 to release/v8

Hints from issue comments:
@matttrach: this has been released

Solution patch:
diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md
index e5acc9f3..1c0c0a5c 100644
--- a/docs/resources/cluster.md
+++ b/docs/resources/cluster.md
@@ -619,12 +619,13 @@ The following arguments are supported:
 * `agent_env_vars` - (Optional) Optional Agent Env Vars for Rancher agent. For Rancher v2.5.6 and above (list)
 * `cluster_agent_deployment_customization` - (Optional) Optional customization for cluster agent. For Rancher v2.7.5 and above (list)
 * `fleet_agent_deployment_customization` - (Optional) Optional customization for fleet agent. For Rancher v2.7.5 and above (list)
-* `rke_config` - (Optional/Computed) The RKE configuration for `rke` Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `gke_config_v2` and `k3s_config` (list maxitems:1)
-* `rke2_config` - (Optional/Computed) The RKE2 configuration for `rke2` Clusters. Conflicts with `aks_config_v2`, `k3s_config` and `rke_config` (list maxitems:1)
-* `k3s_config` - (Optional/Computed) The K3S configuration for `k3s` imported Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `gke_config_v2` and `rke_config` (list maxitems:1)
-* `aks_config_v2` - (Optional) The Azure AKS v2 configuration for creating/import `aks` Clusters. Conflicts with `eks_config_v2`, `gke_config_v2`, `k3s_config` and `rke_config` (list maxitems:1)
-* `eks_config_v2` - (Optional/Computed) The Amazon EKS V2 configuration to create or import `eks` Clusters. Conflicts with `gke_config_v2`, `k3s_config` and `rke_config`. For Rancher v2.5.x and above (list maxitems:1)
-* `gke_config_v2` - (Optional) The Google GKE V2 configuration for `gke` Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `k3s_config` and `rke_config`. For Rancher v2.5.8 and above (list maxitems:1)
+* `rke_config` - (Optional/Computed) The RKE configuration for `rke` Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `gke_config_v2`, `oke_config` and `k3s_config` (list maxitems:1)
+* `rke2_config` - (Optional/Computed) The RKE2 configuration for `rke2` Clusters. Conflicts with `aks_config_v2`, `k3s_config`, `oke_config` and `rke_config` (list maxitems:1)
+* `k3s_config` - (Optional/Computed) The K3S configuration for `k3s` imported Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `gke_config_v2`, `oke_config` and `rke_config` (list maxitems:1)
+* `aks_config_v2` - (Optional) The Azure AKS v2 configuration for creating/import `aks` Clusters. Conflicts with `eks_config_v2`, `gke_config_v2`, `k3s_config`, `oke_config` and `rke_config` (list maxitems:1)
+* `eks_config_v2` - (Optional/Computed) The Amazon EKS V2 configuration to create or import `eks` Clusters. Conflicts with `gke_config_v2`, `k3s_config`, `oke_config` and `rke_config`. For Rancher v2.5.x and above (list maxitems:1)
+* `gke_config_v2` - (Optional) The Google GKE V2 configuration for `gke` Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `k3s_config`, `oke_config` and `rke_config`. For Rancher v2.5.8 and above (list maxitems:1)
+* `oke_config` - (Optional) The Oracle OKE configuration for `oke` Clusters. Conflicts with `aks_config_v2`, `eks_config_v2`, `gke_config_v2`, `k3s_config` and `rke_config` (list maxitems:1)
 * `description` - (Optional) The description for Cluster (string)
 * `cluster_auth_endpoint` - (Optional/Computed) Enabling the [local cluster authorized endpoint](https://rancher.com/docs/rancher/v2.x/en/cluster-provisioning/rke-clusters/options/#local-cluster-auth-endpoint) allows direct communication with the cluster, bypassing the Rancher API proxy. (list maxitems:1)
 * `cluster_template_answers` - (Optional/Computed) Cluster template answers. For Rancher v2.3.x and above (list maxitems:1)
@@ -1759,6 +1760,45 @@ The following arguments are supported:
 * `enable_private_endpoint` - (Optional) Enable GKE cluster private endpoint. Default: `false` (bool)
 * `enable_private_nodes` - (Optional) Enable GKE cluster private endpoint. Default: `false` (bool)
 
+### `oke_config`
+
+#### Arguments
+
+The following arguments are supported:
+
+* `compartment_id` - (Required) The OCID of the compartment in which to create resources OKE cluster and related resources (string)
+* `custom_boot_volume_size` - (Optional) Optional custom boot volume size (GB) for all nodes. If you specify 0, it will apply the default according to the `node_image` specified. Default `0` (int)
+* `description` - (Optional) An optional description of this cluster (string)
+* `enable_private_control_plane` - (Optional) Specifies whether Kubernetes API endpoint is a private IP only accessible from within the VCN. Default `false` for Rancher v2.5.10 and above (bool)
+* `enable_kubernetes_dashboard` - (Optional) Specifies whether to enable the Kubernetes dashboard. Default `false` (bool)
+* `enable_private_nodes` - (Optional) Specifies whether worker nodes will be deployed into a new, private, subnet. Default `false` (bool)
+* `fingerprint` - (Required) The fingerprint corresponding to the specified user's private API Key (string)
+* `flex_ocpus` - (Optional) Specifies number of OCPUs for nodes (requires flexible shape specified with `node_shape`) (int)
+* `kms_key_id` - (Optional) The OCID of a KMS vault master key used to encrypt secrets at rest. See [here](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengencryptingdata.htm) for help creating a vault and master encryption key. For Rancher v2.5.9 and above (string)
+* `kubernetes_version` - (Required) The Kubernetes version that will be used for your master *and* OKE worker nodes (string)
+* `limit_node_count` - (Optional) The maximum number of worker nodes. Can limit `quantity_per_subnet`. Default `0` (no limit) (int)
+* `load_balancer_subnet_name_1` - (Optional) The name of the first existing subnet to use for Kubernetes services / LB. `vcn_name` is also required when specifying an existing subnet. (string)
+* `load_balancer_subnet_name_2` - (Optional) The name of a second existing subnet to use for Kubernetes services / LB. A second subnet is only required when it is AD-specific (non-regional) (string)
+* `node_image` - (Required) The Oracle Linux OS image name to use for the OKE node(s). See [here](https://docs.cloud.oracle.com/en-us/iaas/images/) for a list of images. (string)
+* `node_pool_dns_domain_name` - (Optional) Name for DNS domain of node pool subnet. Default `nodedns` (string)
+* `node_pool_subnet_name` - (Optional) Name for node pool subnet. Default `nodedns` (string)
+* `node_public_key_contents` - (Optional) The contents of the SSH public key file to use for the nodes (string)
+* `node_shape` - (Required) The shape of the node (determines number of CPUs and  amount of memory on each OKE node) (string)
+* `pod_cidr` - (Optional) A CIDR IP range from which to assign Kubernetes Pod IPs (string)
+* `private_key_contents` - (Required/Sensitive) The private API key file contents for the specified user, in PEM format (string)
+* `private_key_passphrase` - (Optional/Sensitive) The passphrase (if any) of the private key for the OKE cluster (string)
+* `quantity_of_node_subnets` - (Optional) Number of node subnets. Default `1` (int)
+* `quantity_per_subnet` - (Optional) Number of OKE worker nodes in each subnet / availability domain. Default `1` (int)
+* `region` - (Required) The availability domain within the region to host the cluster. See [here](https://docs.cloud.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm) for a list of region names. (string)
+* `service_cidr` - (Optional) A CIDR IP range from which to assign Kubernetes Service IPs (string)
+* `service_dns_domain_name` - (Optional) Name for DNS domain of service subnet. Default `svcdns` (string)
+* `skip_vcn_delete` - (Optional) Specifies whether to skip deleting the virtual cloud network (VCN) on destroy. Default `false` (bool)
+* `tenancy_id` - (Required) The OCID of the tenancy in which to create resources (string)
+* `user_ocid` - (Required) The OCID of a user who has access to the tenancy/compartment (string)
+* `vcn_compartment_id` - (Optional) The OCID of the compartment (if different from `compartment_id`) in which to find the pre-existing virtual network set with `vcn_name`. (string)
+* `vcn_name` - (Optional) The name of an existing virtual network to use for the cluster creation. If set, you must also set `load_balancer_subnet_name_1`. A VCN and subnets will be created if none are specified. (string)
+* `worker_node_ingress_cidr` - (Optional) Additional CIDR from which to allow ingress to worker nodes (string)
+
 ### `cluster_auth_endpoint`
 
 #### Arguments
diff --git a/rancher2/data_source_rancher2_cluster.go b/rancher2/data_source_rancher2_cluster.go
index 3078d064..e10a5d2a 100644
--- a/rancher2/data_source_rancher2_cluster.go
+++ b/rancher2/data_source_rancher2_cluster.go
@@ -84,6 +84,14 @@ func dataSourceRancher2Cluster() *schema.Resource {
 					Schema: clusterGKEConfigV2Fields(),
 				},
 			},
+			"oke_config": {
+				Type:     schema.TypeList,
+				MaxItems: 1,
+				Computed: true,
+				Elem: &schema.Resource{
+					Schema: clusterOKEConfigFields(),
+				},
+			},
 			"default_project_id": {
 				Type:     schema.TypeString,
 				Computed: true,
diff --git a/rancher2/resource_rancher2_cluster.go b/rancher2/resource_rancher2_cluster.go
index 6f4cb32a..ecaf9ee5 100644
--- a/rancher2/resource_rancher2_cluster.go
+++ b/rancher2/resource_rancher2_cluster.go
@@ -312,6 +312,12 @@ func resourceRancher2ClusterUpdate(d *schema.ResourceData, meta interface{}) err
 	case ToLower(clusterDriverGKEV2):
 		gkeConfig := expandClusterGKEConfigV2(d.Get("gke_config_v2").([]interface{}))
 		update["gkeConfig"] = fixClusterGKEConfigV2(structToMap(gkeConfig))
+	case clusterOKEKind:
+		okeConfig, err := expandClusterOKEConfig(d.Get("oke_config").([]interface{}), d.Get("name").(string))
+		if err != nil {
+			return err
+		}
+		update["okeEngineConfig"] = okeConfig
 	case ToLower(clusterDriverRKE):
 		rkeConfig, err := expandClusterRKEConfig(d.Get("rke_config").([]interface{}), d.Get("name").(string))
 		if err != nil {
diff --git a/rancher2/schema_cluster.go b/rancher2/schema_cluster.go
index 5c38b29f..4935a535 100644
--- a/rancher2/schema_cluster.go
+++ b/rancher2/schema_cluster.go
@@ -14,7 +14,7 @@ const (
 )
 
 var (
-	clusterDrivers                = []string{clusterDriverImported, clusterDriverGKEV2, clusterDriverK3S, clusterDriverRKE, clusterDriverRKE2}
+	clusterDrivers                = []string{clusterDriverImported, clusterDriverGKEV2, clusterDriverK3S, clusterDriverOKE, clusterDriverRKE, clusterDriverRKE2}
 	clusterRegistrationTokenNames = []string{clusterRegistrationTokenName, "system"}
 )
 
@@ -25,6 +25,7 @@ type Cluster struct {
 	AmazonElasticContainerServiceConfig *AmazonElasticContainerServiceConfig `json:"amazonElasticContainerServiceConfig,omitempty" yaml:"amazonElasticContainerServiceConfig,omitempty"`
 	AzureKubernetesServiceConfig        *AzureKubernetesServiceConfig        `json:"azureKubernetesServiceConfig,omitempty" yaml:"azureKubernetesServiceConfig,omitempty"`
 	GoogleKubernetesEngineConfig        *GoogleKubernetesEngineConfig        `json:"googleKubernetesEngineConfig,omitempty" yaml:"googleKubernetesEngineConfig,omitempty"`
+	OracleKubernetesEngineConfig        *OracleKubernetesEngineConfig        `json:"okeEngineConfig,omitempty" yaml:"okeEngineConfig,omitempty"`
 }
 
 // Schemas
@@ -304,7 +305,7 @@ func clusterFields() map[string]*schema.Schema {
 			MaxItems:      1,
 			Optional:      true,
 			Computed:      true,
-			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "k3s_config", "rke2_config"},
+			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "k3s_config", "oke_config", "rke2_config"},
 			Elem: &schema.Resource{
 				Schema: clusterRKEConfigFields(),
 			},
@@ -314,7 +315,7 @@ func clusterFields() map[string]*schema.Schema {
 			MaxItems:      1,
 			Optional:      true,
 			Computed:      true,
-			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "k3s_config", "rke_config"},
+			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "k3s_config", "oke_config", "rke_config"},
 			Elem: &schema.Resource{
 				Schema: clusterRKE2ConfigFields(),
 			},
@@ -324,7 +325,7 @@ func clusterFields() map[string]*schema.Schema {
 			MaxItems:      1,
 			Optional:      true,
 			Computed:      true,
-			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "rke_config", "rke2_config"},
+			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "rke_config", "oke_config", "rke2_config"},
 			Elem: &schema.Resource{
 				Schema: clusterK3SConfigFields(),
 			},
@@ -334,7 +335,7 @@ func clusterFields() map[string]*schema.Schema {
 			MaxItems:      1,
 			Optional:      true,
 			Computed:      true,
-			ConflictsWith: []string{"aks_config_v2", "gke_config_v2", "k3s_config", "rke_config", "rke2_config"},
+			ConflictsWith: []string{"aks_config_v2", "gke_config_v2", "k3s_config", "rke_config", "oke_config", "rke2_config"},
 			Elem: &schema.Resource{
 				Schema: clusterEKSConfigV2Fields(),
 			},
@@ -343,7 +344,7 @@ func clusterFields() map[string]*schema.Schema {
 			Type:          schema.TypeList,
 			MaxItems:      1,
 			Optional:      true,
-			ConflictsWith: []string{"eks_config_v2", "gke_config_v2", "k3s_config", "rke_config", "rke2_config"},
+			ConflictsWith: []string{"eks_config_v2", "gke_config_v2", "k3s_config", "rke_config", "oke_config", "rke2_config"},
 			Elem: &schema.Resource{
 				Schema: clusterAKSConfigV2Fields(),
 			},
@@ -352,11 +353,20 @@ func clusterFields() map[string]*schema.Schema {
 			Type:          schema.TypeList,
 			MaxItems:      1,
 			Optional:      true,
-			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "k3s_config", "rke_config", "rke2_config"},
+			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "k3s_config", "rke_config", "oke_config", "rke2_config"},
 			Elem: &schema.Resource{
 				Schema: clusterGKEConfigV2Fields(),
 			},
 		},
+		"oke_config": {
+			Type:          schema.TypeList,
+			MaxItems:      1,
+			Optional:      true,
+			ConflictsWith: []string{"aks_config_v2", "eks_config_v2", "gke_config_v2", "k3s_config", "rke_config", "rke2_config"},
+			Elem: &schema.Resource{
+				Schema: clusterOKEConfigFields(),
+			},
+		},
 		"default_project_id": {
 			Type:     schema.TypeString,
 			Computed: true,
diff --git a/rancher2/schema_cluster_oke_config.go b/rancher2/schema_cluster_oke_config.go
new file mode 100644
index 00000000..77e1f961
--- /dev/null
+++ b/rancher2/schema_cluster_oke_config.go
@@ -0,0 +1,232 @@
+// Copyright 2020 Oracle and/or its affiliates.
+
+package rancher2
+
+import (
+	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
+)
+
+const (
+	clusterOKEKind   = "oke"
+	clusterDriverOKE = "oraclecontainerengine"
+)
+
+//Types
+
+type OracleKubernetesEngineConfig struct {
+	CompartmentID               string `json:"compartmentId,omitempty" yaml:"compartmentId,omitempty"`
+	CustomBootVolumeSize        int64  `json:"customBootVolumeSize,omitempty" yaml:"customBootVolumeSize,omitempty"`
+	Description                 string `json:"description,omitempty" yaml:"description,omitempty"`
+	DisplayName                 string `json:"displayName,omitempty" yaml:"displayName,omitempty"`
+	EnableKubernetesDashboard   bool   `json:"enableKubernetesDashboard,omitempty" yaml:"enableKubernetesDashboard,omitempty"`
+	Fingerprint                 string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`
+	FlexOCPUs                   int64  `json:"flexOcpus,omitempty" yaml:"flexOcpus,omitempty"`
+	KubernetesVersion           string `json:"kubernetesVersion,omitempty" yaml:"kubernetesVersion,omitempty"`
+	DriverName                  string `json:"driverName,omitempty" yaml:"driverName,omitempty"`
+	KMSKeyID                    string `json:"kmsKeyId" yaml:"kmsKeyId"`
+	LimitNodeCount              int64  `json:"limitNodeCount,omitempty" yaml:"limitNodeCount,omitempty"`
+	Name                        string `json:"name,omitempty" yaml:"name,omitempty"`
+	NodeImage                   string `json:"nodeImage,omitempty" yaml:"nodeImage,omitempty"`
+	NodePoolSubnetDNSDomainName string `json:"nodePoolDnsDomainName,omitempty" yaml:"nodePoolDnsDomainName,omitempty"`
+	NodePoolSubnetName          string `json:"nodePoolSubnetName,omitempty" yaml:"nodePoolSubnetName,omitempty"`
+	NodePublicSSHKeyContents    string `json:"nodePublicKeyContents,omitempty" yaml:"nodePublicKeyContents,omitempty"`
+	NodeShape                   string `json:"nodeShape,omitempty" yaml:"nodeShape,omitempty"`
+	PrivateControlPlane         bool   `json:"enablePrivateControlPlane,omitempty" yaml:"enablePrivateControlPlane,omitempty"`
+	PrivateKeyContents          string `json:"privateKeyContents,omitempty" yaml:"privateKeyContents,omitempty"`
+	PrivateKeyPassphrase        string `json:"privateKeyPassphrase,omitempty" yaml:"privateKeyPassphrase,omitempty"`
+	PrivateNodes                bool   `json:"enablePrivateNodes,omitempty" yaml:"enablePrivateNodes,omitempty"`
+	PodCidr                     string `json:"podCidr,omitempty" yaml:"podCidr,omitempty"`
+	QuantityOfSubnets           int64  `json:"quantityOfNodeSubnets,omitempty" yaml:"quantityOfNodeSubnets,omitempty"`
+	QuantityPerSubnet           int64  `json:"quantityPerSubnet,omitempty" yaml:"quantityPerSubnet,omitempty"`
+	Region                      string `json:"region,omitempty" yaml:"region,omitempty"`
+	ServiceCidr                 string `json:"serviceCidr,omitempty" yaml:"serviceCidr,omitempty"`
+	ServiceLBSubnet1Name        string `json:"loadBalancerSubnetName1,omitempty" yaml:"loadBalancerSubnetName1,omitempty"`
+	ServiceLBSubnet2Name        string `json:"loadBalancerSubnetName2,omitempty" yaml:"loadBalancerSubnetName2,omitempty"`
+	ServiceSubnetDNSDomainName  string `json:"serviceDnsDomainName,omitempty" yaml:"serviceDnsDomainName,omitempty"`
+	SkipVCNDelete               bool   `json:"skipVcnDelete,omitempty" yaml:"skipVcnDelete,omitempty"`
+	TenancyID                   string `json:"tenancyId,omitempty" yaml:"tenancyId,omitempty"`
+	UserOCID                    string `json:"userOcid,omitempty" yaml:"userOcid,omitempty"`
+	VCNName                     string `json:"vcnName,omitempty" yaml:"vcnName,omitempty"`
+	VcnCompartmentID            string `json:"vcnCompartmentId,omitempty" yaml:"vcnCompartmentId,omitempty"`
+	WorkerNodeIngressCidr       string `json:"workerNodeIngressCidr,omitempty" yaml:"workerNodeIngressCidr,omitempty"`
+}
+
+//Schemas
+
+func clusterOKEConfigFields() map[string]*schema.Schema {
+	s := map[string]*schema.Schema{
+
+		"compartment_id": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The OCID of the compartment in which to create resources (VCN, worker nodes, etc.)",
+		},
+		"custom_boot_volume_size": {
+			Type:        schema.TypeInt,
+			Optional:    true,
+			Description: "An optional custom boot volume size (in GB) for the nodes",
+		},
+		"fingerprint": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The fingerprint corresponding to the specified user's private API Key",
+		},
+		"flex_ocpus": {
+			Type:        schema.TypeInt,
+			Optional:    true,
+			Description: "Optional number of OCPUs for nodes (requires flexible node_shape)",
+		},
+		"kms_key_id": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Sensitive:   true,
+			Description: "Optional specify the OCID of the KMS Vault master key",
+		},
+		"kubernetes_version": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The Kubernetes version that will be used for your master *and* worker nodes e.g. v1.19.7",
+		},
+		"limit_node_count": {
+			Type:        schema.TypeInt,
+			Optional:    true,
+			Description: "Optional limit on the total number of nodes in the pool",
+		},
+		"node_image": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The OS for the node image",
+		},
+		"node_shape": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The shape of the node (determines number of CPUs and  amount of memory on each node)",
+		},
+		"private_key_contents": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Sensitive:   true,
+			Description: "The private API key file contents for the specified user, in PEM format",
+		},
+		"region": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The availability domain within the region to host the OKE cluster",
+		},
+		"tenancy_id": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The OCID of the tenancy in which to create resources",
+		},
+		"user_ocid": {
+			Type:        schema.TypeString,
+			Required:    true,
+			Description: "The OCID of a user who has access to the tenancy/compartment",
+		},
+		"description": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "An optional description of this cluster",
+		},
+		"enable_private_control_plane": {
+			Type:        schema.TypeBool,
+			Optional:    true,
+			Description: "Whether Kubernetes API endpoint is a private IP only accessible from within the VCN",
+		},
+		"enable_kubernetes_dashboard": {
+			Type:        schema.TypeBool,
+			Optional:    true,
+			Default:     false,
+			Description: "Enable the kubernetes dashboard",
+		},
+		"enable_private_nodes": {
+			Type:        schema.TypeBool,
+			Optional:    true,
+			Default:     false,
+			Description: "Whether worker nodes are deployed into a new private subnet",
+		},
+		"load_balancer_subnet_name_1": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "The name of the first existing subnet to use for Kubernetes services / LB",
+		},
+		"load_balancer_subnet_name_2": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "The (optional) name of a second existing subnet to use for Kubernetes services / LB",
+		},
+		"node_pool_dns_domain_name": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Default:     "nodedns",
+			Description: "Optional name for DNS domain of node pool subnet",
+		},
+		"node_pool_subnet_name": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Default:     "nodedns",
+			Description: "Optional name for node pool subnet",
+		},
+		"node_public_key_contents": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "The contents of the SSH public key file to use for the nodes",
+		},
+		"private_key_passphrase": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Sensitive:   true,
+			Description: "The passphrase of the private key for the OKE cluster",
+		},
+		"quantity_of_node_subnets": {
+			Type:        schema.TypeInt,
+			Optional:    true,
+			Description: "Number of node subnets (defaults to creating 1 regional subnet)",
+		},
+		"quantity_per_subnet": {
+			Type:        schema.TypeInt,
+			Optional:    true,
+			Default:     1,
+			Description: "Number of worker nodes in each subnet / availability domain",
+		},
+		"service_dns_domain_name": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Default:     "svcdns",
+			Description: "Optional name for DNS domain of service subnet",
+		},
+		"skip_vcn_delete": {
+			Type:        schema.TypeBool,
+			Optional:    true,
+			Default:     false,
+			Description: "Whether to skip deleting VCN",
+		},
+		"vcn_compartment_id": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "The OCID of the compartment (if different from compartment_id) in which to find the pre-existing virtual network set with vcn_name.",
+		},
+		"vcn_name": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "The optional name of an existing virtual network to use for the cluster creation. A new VCN will be created if not specified.",
+		},
+		"worker_node_ingress_cidr": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "Additional CIDR from which to allow ingress to worker nodes",
+		},
+		"pod_cidr": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "Optional specify the pod CIDR, defaults to 10.244.0.0/16",
+		},
+		"service_cidr": {
+			Type:        schema.TypeString,
+			Optional:    true,
+			Description: "Optional specify the service CIDR, defaults to 10.96.0.0/16",
+		},
+	}
+
+	return s
+}
diff --git a/rancher2/structure_cluster.go b/rancher2/structure_cluster.go
index aa39c0ae..d24bd9b3 100644
--- a/rancher2/structure_cluster.go
+++ b/rancher2/structure_cluster.go
@@ -181,6 +181,20 @@ func flattenCluster(d *schema.ResourceData, in *Cluster, clusterRegToken *manage
 		if err != nil {
 			return err
 		}
+	case clusterOKEKind, clusterDriverOKE:
+		v, ok := d.Get("oke_config").([]interface{})
+		if !ok {
+			v = []interface{}{}
+		}
+
+		okeConfig, err := flattenClusterOKEConfig(in.OracleKubernetesEngineConfig, v)
+		if err != nil {
+			return err
+		}
+		err = d.Set("oke_config", okeConfig)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Setting k3s_config, rke2_config and rke_config always as computed
@@ -452,6 +466,15 @@ func expandCluster(in *schema.ResourceData) (*Cluster, error) {
 		obj.Driver = clusterDriverGKEV2
 	}
 
+	if v, ok := in.Get("oke_config").([]interface{}); ok && len(v) > 0 {
+		okeConfig, err := expandClusterOKEConfig(v, obj.Name)
+		if err != nil {
+			return nil, err
+		}
+		obj.OracleKubernetesEngineConfig = okeConfig
+		obj.Driver = clusterOKEKind
+	}
+
 	if v, ok := in.Get("k3s_config").([]interface{}); ok && len(v) > 0 {
 		obj.K3sConfig = expandClusterK3SConfig(v)
 		obj.Driver = clusterDriverK3S
diff --git a/rancher2/structure_cluster_oke_config.go b/rancher2/structure_cluster_oke_config.go
new file mode 100644
index 00000000..39abfabb
--- /dev/null
+++ b/rancher2/structure_cluster_oke_config.go
@@ -0,0 +1,286 @@
+// Copyright 2020 Oracle and/or its affiliates.
+
+package rancher2
+
+// Flatteners
+
+func flattenClusterOKEConfig(in *OracleKubernetesEngineConfig, p []interface{}) ([]interface{}, error) {
+	var obj map[string]interface{}
+	if len(p) == 0 || p[0] == nil {
+		obj = make(map[string]interface{})
+	} else {
+		obj = p[0].(map[string]interface{})
+	}
+
+	if in == nil {
+		return []interface{}{}, nil
+	}
+
+	if len(in.CompartmentID) > 0 {
+		obj["compartment_id"] = in.CompartmentID
+	}
+
+	if in.CustomBootVolumeSize > 0 {
+		obj["custom_boot_volume_size"] = int(in.CustomBootVolumeSize)
+	}
+
+	if len(in.Description) > 0 {
+		obj["description"] = in.Description
+	}
+
+	obj["enable_private_control_plane"] = in.PrivateControlPlane
+	obj["enable_kubernetes_dashboard"] = in.EnableKubernetesDashboard
+	obj["enable_private_nodes"] = in.PrivateNodes
+
+	if len(in.Fingerprint) > 0 {
+		obj["fingerprint"] = in.Fingerprint
+	}
+
+	if in.FlexOCPUs > 0 {
+		obj["flex_ocpus"] = int(in.FlexOCPUs)
+	}
+
+	if len(in.KMSKeyID) > 0 {
+		obj["kms_key_id"] = in.KMSKeyID
+	}
+
+	if len(in.KubernetesVersion) > 0 {
+		obj["kubernetes_version"] = in.KubernetesVersion
+	}
+
+	if in.LimitNodeCount > 0 {
+		obj["limit_node_count"] = int(in.LimitNodeCount)
+	}
+
+	if len(in.ServiceLBSubnet1Name) > 0 {
+		obj["load_balancer_subnet_name_1"] = in.ServiceLBSubnet1Name
+	}
+
+	if len(in.ServiceLBSubnet2Name) > 0 {
+		obj["load_balancer_subnet_name_2"] = in.ServiceLBSubnet2Name
+	}
+
+	if len(in.NodeImage) > 0 {
+		obj["node_image"] = in.NodeImage
+	}
+
+	if len(in.NodePoolSubnetDNSDomainName) > 0 {
+		obj["node_pool_dns_domain_name"] = in.NodePoolSubnetDNSDomainName
+	}
+
+	if len(in.NodePoolSubnetName) > 0 {
+		obj["node_pool_subnet_name"] = in.NodePoolSubnetName
+	}
+
+	if len(in.NodePublicSSHKeyContents) > 0 {
+		obj["node_public_key_contents"] = in.NodePublicSSHKeyContents
+	}
+
+	if len(in.NodeShape) > 0 {
+		obj["node_shape"] = in.NodeShape
+	}
+
+	if len(in.PodCidr) > 0 {
+		obj["pod_cidr"] = in.PodCidr
+	}
+
+	if len(in.PrivateKeyContents) > 0 {
+		obj["private_key_contents"] = in.PrivateKeyContents
+	}
+
+	if len(in.PrivateKeyPassphrase) > 0 {
+		obj["private_key_passphrase"] = in.PrivateKeyPassphrase
+	}
+
+	if in.QuantityOfSubnets > 0 {
+		obj["quantity_of_node_subnets"] = int(in.QuantityOfSubnets)
+	}
+
+	if in.QuantityPerSubnet > 0 {
+		obj["quantity_per_subnet"] = int(in.QuantityPerSubnet)
+	}
+
+	if len(in.Region) > 0 {
+		obj["region"] = in.Region
+	}
+
+	if len(in.ServiceCidr) > 0 {
+		obj["service_cidr"] = in.ServiceCidr
+	}
+
+	if len(in.ServiceSubnetDNSDomainName) > 0 {
+		obj["service_dns_domain_name"] = in.ServiceSubnetDNSDomainName
+	}
+
+	obj["skip_vcn_delete"] = in.SkipVCNDelete
+
+	if len(in.TenancyID) > 0 {
+		obj["tenancy_id"] = in.TenancyID
+	}
+
+	if len(in.UserOCID) > 0 {
+		obj["user_ocid"] = in.UserOCID
+	}
+
+	if len(in.VcnCompartmentID) > 0 {
+		obj["vcn_compartment_id"] = in.VcnCompartmentID
+	}
+
+	if len(in.VCNName) > 0 {
+		obj["vcn_name"] = in.VCNName
+	}
+
+	if len(in.WorkerNodeIngressCidr) > 0 {
+		obj["worker_node_ingress_cidr"] = in.WorkerNodeIngressCidr
+	}
+
+	return []interface{}{obj}, nil
+}
+
+// Expanders
+
+func expandClusterOKEConfig(p []interface{}, name string) (*OracleKubernetesEngineConfig, error) {
+	obj := &OracleKubernetesEngineConfig{}
+	if len(p) == 0 || p[0] == nil {
+		return obj, nil
+	}
+	in := p[0].(map[string]interface{})
+
+	obj.DisplayName = name
+	obj.Name = name
+	obj.DriverName = clusterDriverOKE
+
+	if v, ok := in["compartment_id"].(string); ok && len(v) > 0 {
+		obj.CompartmentID = v
+	}
+
+	if v, ok := in["custom_boot_volume_size"].(int); ok && v > 0 {
+		obj.CustomBootVolumeSize = int64(v)
+	}
+
+	if v, ok := in["description"].(string); ok && len(v) > 0 {
+		obj.Description = v
+	}
+
+	if v, ok := in["enable_private_control_plane"].(bool); ok {
+		obj.PrivateControlPlane = v
+	}
+
+	if v, ok := in["enable_kubernetes_dashboard"].(bool); ok {
+		obj.EnableKubernetesDashboard = v
+	}
+
+	if v, ok := in["enable_private_nodes"].(bool); ok {
+		obj.PrivateNodes = v
+	}
+
+	if v, ok := in["fingerprint"].(string); ok && len(v) > 0 {
+		obj.Fingerprint = v
+	}
+
+	if v, ok := in["flex_ocpus"].(int); ok && v > 0 {
+		obj.FlexOCPUs = int64(v)
+	}
+
+	if v, ok := in["kms_key_id"].(string); ok && len(v) > 0 {
+		obj.KMSKeyID = v
+	}
+
+	if v, ok := in["kubernetes_version"].(string); ok && len(v) > 0 {
+		obj.KubernetesVersion = v
+	}
+
+	if v, ok := in["limit_node_count"].(int); ok && v > 0 {
+		obj.LimitNodeCount = int64(v)
+	}
+
+	if v, ok := in["load_balancer_subnet_name_1"].(string); ok && len(v) > 0 {
+		obj.ServiceLBSubnet1Name = v
+	}
+
+	if v, ok := in["load_balancer_subnet_name_2"].(string); ok && len(v) > 0 {
+		obj.ServiceLBSubnet2Name = v
+	}
+
+	if v, ok := in["name"].(string); ok && len(v) > 0 {
+		obj.Name = v
+	}
+
+	if v, ok := in["node_image"].(string); ok && len(v) > 0 {
+		obj.NodeImage = v
+	}
+
+	if v, ok := in["node_pool_dns_domain_name"].(string); ok && len(v) > 0 {
+		obj.NodePoolSubnetDNSDomainName = v
+	}
+
+	if v, ok := in["node_pool_subnet_name"].(string); ok && len(v) > 0 {
+		obj.NodePoolSubnetName = v
+	}
+
+	if v, ok := in["node_public_key_contents"].(string); ok && len(v) > 0 {
+		obj.NodePublicSSHKeyContents = v
+	}
+
+	if v, ok := in["node_shape"].(string); ok && len(v) > 0 {
+		obj.NodeShape = v
+	}
+
+	if v, ok := in["pod_cidr"].(string); ok && len(v) > 0 {
+		obj.PodCidr = v
+	}
+
+	if v, ok := in["private_key_contents"].(string); ok && len(v) > 0 {
+		obj.PrivateKeyContents = v
+	}
+
+	if v, ok := in["private_key_passphrase"].(string); ok && len(v) > 0 {
+		obj.PrivateKeyPassphrase = v
+	}
+
+	if v, ok := in["quantity_of_node_subnets"].(int); ok && v > 0 {
+		obj.QuantityOfSubnets = int64(v)
+	}
+
+	if v, ok := in["quantity_per_subnet"].(int); ok && v > 0 {
+		obj.QuantityPerSubnet = int64(v)
+	}
+
+	if v, ok := in["region"].(string); ok && len(v) > 0 {
+		obj.Region = v
+	}
+
+	if v, ok := in["service_dns_domain_name"].(string); ok && len(v) > 0 {
+		obj.ServiceSubnetDNSDomainName = v
+	}
+
+	if v, ok := in["service_cidr"].(string); ok && len(v) > 0 {
+		obj.ServiceCidr = v
+	}
+
+	if v, ok := in["skip_vcn_delete"].(bool); ok {
+		obj.SkipVCNDelete = v
+	}
+
+	if v, ok := in["tenancy_id"].(string); ok && len(v) > 0 {
+		obj.TenancyID = v
+	}
+
+	if v, ok := in["user_ocid"].(string); ok && len(v) > 0 {
+		obj.UserOCID = v
+	}
+
+	if v, ok := in["vcn_compartment_id"].(string); ok && len(v) > 0 {
+		obj.VcnCompartmentID = v
+	}
+
+	if v, ok := in["vcn_name"].(string); ok && len(v) > 0 {
+		obj.VCNName = v
+	}
+
+	if v, ok := in["worker_node_ingress_cidr"].(string); ok && len(v) > 0 {
+		obj.WorkerNodeIngressCidr = v
+	}
+
+	return obj, nil
+}



Test patch:
diff --git a/rancher2/structure_cluster_oke_config_test.go b/rancher2/structure_cluster_oke_config_test.go
new file mode 100644
index 00000000..33c58185
--- /dev/null
+++ b/rancher2/structure_cluster_oke_config_test.go
@@ -0,0 +1,131 @@
+// Copyright 2020 Oracle and/or its affiliates.
+
+package rancher2
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	testClusterOKEConfigConf      *OracleKubernetesEngineConfig
+	testClusterOKEConfigInterface []interface{}
+)
+
+func init() {
+	testClusterOKEConfigConf = &OracleKubernetesEngineConfig{
+		CompartmentID:               "compartment",
+		CustomBootVolumeSize:        0,
+		Description:                 "description",
+		DisplayName:                 "test",
+		DriverName:                  clusterDriverOKE,
+		EnableKubernetesDashboard:   true,
+		Fingerprint:                 "fingerprint",
+		FlexOCPUs:                   0,
+		KubernetesVersion:           "version",
+		KMSKeyID:                    "ocid1.key.oc1.reg.xxxxxxxxxxxxxxxxxx",
+		LimitNodeCount:              0,
+		Name:                        "test",
+		NodeImage:                   "image",
+		NodePoolSubnetDNSDomainName: "nodedns",
+		NodePoolSubnetName:          "nodedns",
+		NodePublicSSHKeyContents:    "public_key",
+		NodeShape:                   "shape",
+		PodCidr:                     "10.204.0.0/16",
+		PrivateKeyContents:          "private_key_contents",
+		PrivateKeyPassphrase:        "",
+		PrivateNodes:                false,
+		QuantityOfSubnets:           2,
+		QuantityPerSubnet:           1,
+		Region:                      "region",
+		ServiceCidr:                 "10.98.0.0/16",
+		ServiceLBSubnet1Name:        "",
+		ServiceLBSubnet2Name:        "",
+		ServiceSubnetDNSDomainName:  "svcdns",
+		SkipVCNDelete:               false,
+		TenancyID:                   "tenancy",
+		UserOCID:                    "user",
+		VCNName:                     "",
+		VcnCompartmentID:            "",
+		WorkerNodeIngressCidr:       "",
+	}
+	testClusterOKEConfigInterface = []interface{}{
+		map[string]interface{}{
+			"compartment_id":               "compartment",
+			"custom_boot_volume_size":      0,
+			"description":                  "description",
+			"enable_private_control_plane": false,
+			"enable_kubernetes_dashboard":  true,
+			"enable_private_nodes":         false,
+			"fingerprint":                  "fingerprint",
+			"flex_ocpus":                   0,
+			"kms_key_id":                   "ocid1.key.oc1.reg.xxxxxxxxxxxxxxxxxx",
+			"kubernetes_version":           "version",
+			"limit_node_count":             0,
+			"node_image":                   "image",
+			"node_pool_dns_domain_name":    "nodedns",
+			"node_pool_subnet_name":        "nodedns",
+			"node_public_key_contents":     "public_key",
+			"node_shape":                   "shape",
+			"pod_cidr":                     "10.204.0.0/16",
+			"private_key_contents":         "private_key_contents",
+			"private_key_passphrase":       "",
+			"quantity_of_node_subnets":     2,
+			"quantity_per_subnet":          1,
+			"region":                       "region",
+			"load_balancer_subnet_name_1":  "",
+			"load_balancer_subnet_name_2":  "",
+			"service_cidr":                 "10.98.0.0/16",
+			"service_dns_domain_name":      "svcdns",
+			"skip_vcn_delete":              false,
+			"tenancy_id":                   "tenancy",
+			"user_ocid":                    "user",
+			"vcn_compartment_id":           "",
+			"vcn_name":                     "",
+			"worker_node_ingress_cidr":     "",
+		},
+	}
+}
+
+func TestFlattenClusterOKEConfig(t *testing.T) {
+
+	cases := []struct {
+		Input          *OracleKubernetesEngineConfig
+		ExpectedOutput []interface{}
+	}{
+		{
+			testClusterOKEConfigConf,
+			testClusterOKEConfigInterface,
+		},
+	}
+
+	for _, tc := range cases {
+		output, err := flattenClusterOKEConfig(tc.Input, testClusterOKEConfigInterface)
+		if err != nil {
+			assert.FailNow(t, "[ERROR] on flattener: %#v", err)
+		}
+		assert.Equal(t, tc.ExpectedOutput, output, "Unexpected output from flattener.")
+	}
+}
+
+func TestExpandClusterOKEConfig(t *testing.T) {
+
+	cases := []struct {
+		Input          []interface{}
+		ExpectedOutput *OracleKubernetesEngineConfig
+	}{
+		{
+			testClusterOKEConfigInterface,
+			testClusterOKEConfigConf,
+		},
+	}
+
+	for _, tc := range cases {
+		output, err := expandClusterOKEConfig(tc.Input, "test")
+		if err != nil {
+			assert.FailNow(t, "[ERROR] on expander: %#v", err)
+		}
+		assert.Equal(t, tc.ExpectedOutput, output, "Unexpected output from expander.")
+	}
+}
diff --git a/rancher2/structure_cluster_test.go b/rancher2/structure_cluster_test.go
index 0fcca6f7..c542c2e5 100644
--- a/rancher2/structure_cluster_test.go
+++ b/rancher2/structure_cluster_test.go
@@ -383,6 +383,31 @@ func testCluster() {
 		"gke_config_v2":         testClusterGKEConfigV2Interface,
 		"system_project_id":     "system_project_id",
 	}
+	testClusterConfOKE = &Cluster{
+		OracleKubernetesEngineConfig: testClusterOKEConfigConf,
+	}
+	testClusterConfOKE.Name = "test"
+	testClusterConfOKE.Description = "description"
+	testClusterConfOKE.Driver = clusterOKEKind
+	testClusterConfOKE.AgentEnvVars = testClusterEnvVarsConf
+	testClusterConfOKE.DefaultPodSecurityAdmissionConfigurationTemplateName = "default_pod_security_admission_configuration_template_name"
+	testClusterConfOKE.EnableNetworkPolicy = newTrue()
+	testClusterConfOKE.LocalClusterAuthEndpoint = testLocalClusterAuthEndpointConf
+	testClusterInterfaceOKE = map[string]interface{}{
+		"id":                         "id",
+		"name":                       "test",
+		"agent_env_vars":             testClusterEnvVarsInterface,
+		"default_project_id":         "default_project_id",
+		"description":                "description",
+		"cluster_auth_endpoint":      testLocalClusterAuthEndpointInterface,
+		"cluster_registration_token": testClusterRegistrationTokenInterface,
+		"default_pod_security_admission_configuration_template_name": "default_pod_security_admission_configuration_template_name",
+		"enable_network_policy": true,
+		"kube_config":           "kube_config",
+		"driver":                clusterOKEKind,
+		"oke_config":            testClusterOKEConfigInterface,
+		"system_project_id":     "system_project_id",
+	}
 	testClusterConfRKE = &Cluster{}
 	testClusterConfRKE.Name = "test"
 	testClusterConfRKE.Description = "description"
@@ -518,6 +543,12 @@ func TestFlattenCluster(t *testing.T) {
 			testClusterGenerateKubeConfigOutput,
 			testClusterInterfaceGKEV2,
 		},
+		{
+			testClusterConfOKE,
+			testClusterRegistrationTokenConf,
+			testClusterGenerateKubeConfigOutput,
+			testClusterInterfaceOKE,
+		},
 		{
 			testClusterConfRKE,
 			testClusterRegistrationTokenConf,
@@ -605,6 +636,10 @@ func TestExpandCluster(t *testing.T) {
 			testClusterInterfaceGKEV2,
 			testClusterConfGKEV2,
 		},
+		{
+			testClusterInterfaceOKE,
+			testClusterConfOKE,
+		},
 		{
 			testClusterInterfaceRKE,
 			testClusterConfRKE,