Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kube/OIDC #273

Merged
merged 19 commits into from
Jul 26, 2022
1 change: 1 addition & 0 deletions ovh/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func Provider() *schema.Provider {
"ovh_cloud_project_failover_ip_attach": resourceCloudProjectFailoverIpAttach(),
"ovh_cloud_project_kube": resourceCloudProjectKube(),
"ovh_cloud_project_kube_nodepool": resourceCloudProjectKubeNodePool(),
"ovh_cloud_project_kube_oidc": resourceCloudProjectKubeOIDC(),
"ovh_cloud_project_network_private": resourceCloudProjectNetworkPrivate(),
"ovh_cloud_project_network_private_subnet": resourceCloudProjectNetworkPrivateSubnet(),
"ovh_cloud_project_user": resourceCloudProjectUser(),
Expand Down
36 changes: 29 additions & 7 deletions ovh/resource_cloud_project_kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ import (
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
)

const kubeClusterNameKey = "name"

func resourceCloudProjectKube() *schema.Resource {
return &schema.Resource{
Create: resourceCloudProjectKubeCreate,
Read: resourceCloudProjectKubeRead,
Delete: resourceCloudProjectKubeDelete,
Update: resourceCloudProjectKubeUpdate,

Importer: &schema.ResourceImporter{
State: resourceCloudProjectKubeImportState,
Expand All @@ -30,10 +33,10 @@ func resourceCloudProjectKube() *schema.Resource {
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil),
},
"name": {
kubeClusterNameKey: {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ForceNew: false,
},
"version": {
Type: schema.TypeString,
Expand Down Expand Up @@ -132,7 +135,7 @@ func resourceCloudProjectKubeCreate(d *schema.ResourceData, meta interface{}) er
}

log.Printf("[DEBUG] Waiting for kube %s to be READY", res.Id)
err = waitForCloudProjectKubeReady(config.OVHClient, serviceName, res.Id)
err = waitForCloudProjectKubeReady(config.OVHClient, serviceName, res.Id, []string{"INSTALLING"}, []string{"READY"})
if err != nil {
return fmt.Errorf("timeout while waiting kube %s to be READY: %v", res.Id, err)
}
Expand Down Expand Up @@ -166,7 +169,6 @@ func resourceCloudProjectKubeRead(d *schema.ResourceData, meta interface{}) erro
kubeconfigRaw := CloudProjectKubeKubeConfigResponse{}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/kubeconfig", serviceName, res.Id)
err := config.OVHClient.Post(endpoint, nil, &kubeconfigRaw)

if err != nil {
return err
}
Expand Down Expand Up @@ -201,17 +203,37 @@ func resourceCloudProjectKubeDelete(d *schema.ResourceData, meta interface{}) er
return nil
}

func resourceCloudProjectKubeUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
serviceName := d.Get("service_name").(string)

if d.HasChange(kubeClusterNameKey) {
_, newValue := d.GetChange(kubeClusterNameKey)
value := newValue.(string)

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s", serviceName, d.Id())
err := config.OVHClient.Put(endpoint, CloudProjectKubePutOpts{
Name: &value,
}, nil)
if err != nil {
return err
}
}

return nil
}

func cloudProjectKubeExists(serviceName, id string, client *ovh.Client) error {
res := &CloudProjectKubeResponse{}

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s", serviceName, id)
return client.Get(endpoint, res)
}

func waitForCloudProjectKubeReady(client *ovh.Client, serviceName, kubeId string) error {
func waitForCloudProjectKubeReady(client *ovh.Client, serviceName, kubeId string, pending []string, target []string) error {
stateConf := &resource.StateChangeConf{
Pending: []string{"INSTALLING"},
Target: []string{"READY"},
Pending: pending,
Target: target,
Refresh: func() (interface{}, string, error) {
res := &CloudProjectKubeResponse{}
endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s", serviceName, kubeId)
Expand Down
143 changes: 143 additions & 0 deletions ovh/resource_cloud_project_kube_oidc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package ovh

import (
"fmt"
"log"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCloudProjectKubeOIDC() *schema.Resource {
return &schema.Resource{
Create: resourceCloudProjectKubeOIDCCreate,
Read: resourceCloudProjectKubeOIDCRead,
Delete: resourceCloudProjectKubeOIDCDelete,
Update: resourceCloudProjectKubeOIDCUpdate,

Schema: map[string]*schema.Schema{
"service_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: schema.EnvDefaultFunc("OVH_CLOUD_PROJECT_SERVICE", nil),
},
"kube_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"client_id": {
Type: schema.TypeString,
Required: true,
},
"issuer_url": {
Type: schema.TypeString,
Required: true,
},
},
}
}
matprig marked this conversation as resolved.
Show resolved Hide resolved

func resourceCloudProjectKubeOIDCCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

serviceName := d.Get("service_name").(string)
kubeID := d.Get("kube_id").(string)

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", serviceName, kubeID)
params := (&CloudProjectKubeOIDCCreateOpts{}).FromResource(d)
res := &CloudProjectKubeOIDCResponse{}

log.Printf("[DEBUG] Will create OIDC: %+v", params)
err := config.OVHClient.Post(endpoint, params, res)
if err != nil {
return fmt.Errorf("calling Post %s with params %s:\n\t %w", endpoint, params, err)
}

d.SetId(kubeID + "-" + params.ClientID + "-" + params.IssuerUrl)

log.Printf("[DEBUG] Waiting for kube %s to be READY", kubeID)
err = waitForCloudProjectKubeReady(config.OVHClient, serviceName, kubeID, []string{"REDEPLOYING"}, []string{"READY"})
if err != nil {
return fmt.Errorf("timeout while waiting kube %s to be READY: %w", kubeID, err)
}
log.Printf("[DEBUG] kube %s is READY", kubeID)

return resourceCloudProjectKubeOIDCRead(d, meta)
}

func resourceCloudProjectKubeOIDCRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

serviceName := d.Get("service_name").(string)
kubeID := d.Get("kube_id").(string)

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", serviceName, kubeID)
res := &CloudProjectKubeOIDCResponse{}

log.Printf("[DEBUG] Will read oidc from kube %s and project: %s", kubeID, serviceName)
err := config.OVHClient.Get(endpoint, res)
if err != nil {
return fmt.Errorf("calling get %s %w", endpoint, err)
}
for k, v := range res.ToMap() {
if k != "id" {
d.Set(k, v)
} else {
d.SetId(kubeID + "-" + res.ClientID + "-" + res.IssuerUrl)
}
}

log.Printf("[DEBUG] Read kube %+v", res)
return nil
}

func resourceCloudProjectKubeOIDCUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

serviceName := d.Get("service_name").(string)
kubeID := d.Get("kube_id").(string)

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", serviceName, kubeID)
params := (&CloudProjectKubeOIDCUpdateOpts{}).FromResource(d)
res := &CloudProjectKubeOIDCResponse{}

log.Printf("[DEBUG] Will update OIDC: %+v", params)
err := config.OVHClient.Put(endpoint, params, res)
if err != nil {
return fmt.Errorf("calling Put %s with params %s:\n\t %w", endpoint, params, err)
}

log.Printf("[DEBUG] Waiting for kube %s to be READY", kubeID)
err = waitForCloudProjectKubeReady(config.OVHClient, serviceName, kubeID, []string{"REDEPLOYING"}, []string{"READY"})
if err != nil {
return fmt.Errorf("timeout while waiting kube %s to be READY: %w", kubeID, err)
}
log.Printf("[DEBUG] kube %s is READY", kubeID)

return nil
}

func resourceCloudProjectKubeOIDCDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

serviceName := d.Get("service_name").(string)
kubeID := d.Get("kube_id").(string)

endpoint := fmt.Sprintf("/cloud/project/%s/kube/%s/openIdConnect", serviceName, kubeID)

log.Printf("[DEBUG] Will delete OIDC")
err := config.OVHClient.Delete(endpoint, nil)
if err != nil {
return fmt.Errorf("calling delete %s %w", endpoint, err)
}

log.Printf("[DEBUG] Waiting for kube %s to be READY", kubeID)
err = waitForCloudProjectKubeReady(config.OVHClient, serviceName, kubeID, []string{"REDEPLOYING"}, []string{"READY"})
if err != nil {
return fmt.Errorf("timeout while waiting kube %s to be READY: %w", kubeID, err)
}
log.Printf("[DEBUG] kube %s is READY", kubeID)

return nil
}
81 changes: 81 additions & 0 deletions ovh/resource_cloud_project_kube_oidc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package ovh

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

var testAccCloudProjectKubeOIDCConfig = `
resource "ovh_cloud_project_kube" "cluster" {
service_name = "%s"
name = "%s"
region = "%s"
}
resource "ovh_cloud_project_kube_oidc" "my-oidc" {
service_name = ovh_cloud_project_kube.cluster.service_name
kube_id = ovh_cloud_project_kube.cluster.id
client_id = "%s"
issuer_url = "%s"
}
`

func TestAccCloudProjectKubeOIDC_full(t *testing.T) {
region := os.Getenv("OVH_CLOUD_PROJECT_KUBE_REGION_TEST")

name := acctest.RandomWithPrefix(test_prefix)
config := fmt.Sprintf(
testAccCloudProjectKubeOIDCConfig,
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
name,
region,
"my-oidc-client-id",
"https://ovh.com",
)

configUpdated := fmt.Sprintf(
testAccCloudProjectKubeOIDCConfig,
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
name,
region,
"my-another-oidc-client-id",
"https://docs.ovh.com",
)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheckCloud(t)
testAccCheckCloudProjectExists(t)
testAccPreCheckKubernetes(t)
},
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube_oidc.my-oidc", "client_id", "my-oidc-client-id"),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube_oidc.my-oidc", "issuer_url", "https://ovh.com"),
),
},
{
Config: configUpdated,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube_oidc.my-oidc", "client_id", "my-another-oidc-client-id"),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube_oidc.my-oidc", "issuer_url", "https://docs.ovh.com"),
),
},
{
Config: configUpdated,
Destroy: true,
ResourceName: "ovh_cloud_project_kube_oidc.my-oidc",
},
},
})
}
34 changes: 31 additions & 3 deletions ovh/resource_cloud_project_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ resource "ovh_cloud_project_kube" "cluster" {
version = "%s"
}
`

var testAccCloudProjectKubeEmptyVersionConfig = `
resource "ovh_cloud_project_kube" "cluster" {
service_name = "%s"
Expand Down Expand Up @@ -108,7 +109,7 @@ func TestAccCloudProjectKube_basic(t *testing.T) {
resource.TestCheckResourceAttrSet(
"ovh_cloud_project_kube.cluster", "kubeconfig"),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube.cluster", "name", name),
"ovh_cloud_project_kube.cluster", kubeClusterNameKey, name),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube.cluster", "version", version),
),
Expand All @@ -117,16 +118,30 @@ func TestAccCloudProjectKube_basic(t *testing.T) {
})
}

// TestAccCloudProjectKubeEmptyVersion_basic
// create a public cluster
// check some properties
// update cluster name
// check some properties && cluster updated name
func TestAccCloudProjectKubeEmptyVersion_basic(t *testing.T) {
name := acctest.RandomWithPrefix(test_prefix)
region := os.Getenv("OVH_CLOUD_PROJECT_KUBE_REGION_TEST")

name := acctest.RandomWithPrefix(test_prefix)
config := fmt.Sprintf(
testAccCloudProjectKubeEmptyVersionConfig,
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
name,
region,
)

updatedName := acctest.RandomWithPrefix(test_prefix)
updatedConfig := fmt.Sprintf(
testAccCloudProjectKubeEmptyVersionConfig,
os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"),
updatedName,
region,
)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheckCloud(t)
Expand All @@ -143,7 +158,20 @@ func TestAccCloudProjectKubeEmptyVersion_basic(t *testing.T) {
resource.TestCheckResourceAttrSet(
"ovh_cloud_project_kube.cluster", "kubeconfig"),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube.cluster", "name", name),
"ovh_cloud_project_kube.cluster", kubeClusterNameKey, name),
resource.TestCheckResourceAttrSet(
"ovh_cloud_project_kube.cluster", "version"),
),
},
{
Config: updatedConfig,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube.cluster", "region", region),
resource.TestCheckResourceAttrSet(
"ovh_cloud_project_kube.cluster", "kubeconfig"),
resource.TestCheckResourceAttr(
"ovh_cloud_project_kube.cluster", kubeClusterNameKey, updatedName),
resource.TestCheckResourceAttrSet(
"ovh_cloud_project_kube.cluster", "version"),
),
Expand Down
Loading