Skip to content

Use new generated order schema to order products + add resource ovh_vps #594

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

Merged
merged 2 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ require (
github.com/google/go-cmp v0.6.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/terraform-plugin-framework v1.6.1
github.com/hashicorp/terraform-plugin-framework v1.7.0
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-go v0.22.0
github.com/hashicorp/terraform-plugin-go v0.22.1
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-mux v0.15.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0
github.com/hashicorp/terraform-plugin-testing v1.7.0
Expand Down Expand Up @@ -35,7 +36,6 @@ require (
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-exec v0.20.0 // indirect
github.com/hashicorp/terraform-json v0.21.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
Expand All @@ -60,7 +60,7 @@ require (
golang.org/x/tools v0.13.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,16 @@ github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J
github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw=
github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U=
github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk=
github.com/hashicorp/terraform-plugin-framework v1.4.2 h1:P7a7VP1GZbjc4rv921Xy5OckzhoiO3ig6SGxwelD2sI=
github.com/hashicorp/terraform-plugin-framework v1.4.2/go.mod h1:GWl3InPFZi2wVQmdVnINPKys09s9mLmTZr95/ngLnbY=
github.com/hashicorp/terraform-plugin-framework v1.6.1 h1:hw2XrmUu8d8jVL52ekxim2IqDc+2Kpekn21xZANARLU=
github.com/hashicorp/terraform-plugin-framework v1.6.1/go.mod h1:aJI+n/hBPhz1J+77GdgNfk5svW12y7fmtxe/5L5IuwI=
github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw=
github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
github.com/hashicorp/terraform-plugin-go v0.22.0 h1:1OS1Jk5mO0f5hrziWJGXXIxBrMe2j/B8E+DVGw43Xmc=
github.com/hashicorp/terraform-plugin-go v0.22.0/go.mod h1:mPULV91VKss7sik6KFEcEu7HuTogMLLO/EvWCuFkRVE=
github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w=
github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI=
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=
github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow=
github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME=
Expand Down Expand Up @@ -225,6 +227,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
Expand Down
10 changes: 3 additions & 7 deletions ovh/notification_email.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import (
"strings"
)

func notificationEmailSortedIds(meta interface{}) ([]int64, error) {
config := meta.(*Config)

func notificationEmailSortedIds(config *Config) ([]int64, error) {
// Create Order
log.Printf("[DEBUG] Will read notification emails ids")
res := []int64{}
Expand All @@ -24,10 +22,8 @@ func notificationEmailSortedIds(meta interface{}) ([]int64, error) {
return res, nil
}

func getNewNotificationEmail(matches []string, oldIds []int64, meta interface{}) (*NotificationEmail, error) {
config := meta.(*Config)

curIds, err := notificationEmailSortedIds(meta)
func getNewNotificationEmail(matches []string, oldIds []int64, config *Config) (*NotificationEmail, error) {
curIds, err := notificationEmailSortedIds(config)
if err != nil {
return nil, err
}
Expand Down
163 changes: 110 additions & 53 deletions ovh/order.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package ovh

import (
"errors"
"fmt"
"log"
"net/url"
"regexp"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/ovh/go-ovh/ovh"
"github.com/ovh/terraform-provider-ovh/ovh/helpers"
"github.com/ovh/terraform-provider-ovh/ovh/types"
)

var (
Expand Down Expand Up @@ -218,24 +221,36 @@ func genericOrderSchema(withOptions bool) map[string]*schema.Schema {
return orderSchema
}

func orderCreate(d *schema.ResourceData, meta interface{}, product string) error {
func orderCreateFromResource(d *schema.ResourceData, meta interface{}, product string) error {
config := meta.(*Config)
order := (&OrderModel{}).FromResource(d)

err := orderCreate(order, config, product)
if err != nil {
return err
}

d.SetId(fmt.Sprint(order.Order.OrderId.ValueInt64()))

return nil
}

func orderCreate(d *OrderModel, config *Config, product string) error {
// create Cart
cartParams := &OrderCartCreateOpts{
OvhSubsidiary: strings.ToUpper(d.Get("ovh_subsidiary").(string)),
OvhSubsidiary: strings.ToUpper(d.OvhSubsidiary.ValueString()),
}

cart, err := orderCartCreate(meta, cartParams, true)
cart, err := orderCartCreate(config, cartParams, true)
if err != nil {
return fmt.Errorf("calling creating order cart: %q", err)
}

// Create Product Item
item := &OrderCartItem{}
cartPlanParams := (&OrderCartPlanCreateOpts{
Quantity: 1,
}).FromResourceWithPath(d, "plan.0")
cartPlanParamsList := d.Plan.Elements()
cartPlanParams := cartPlanParamsList[0].(PlanValue)
cartPlanParams.Quantity = types.TfInt64Value{Int64Value: basetypes.NewInt64Value(1)}

log.Printf("[DEBUG] Will create order item %s for cart: %s", product, cart.CartId)
endpoint := fmt.Sprintf("/order/cart/%s/%s", url.PathEscape(cart.CartId), product)
Expand All @@ -244,62 +259,53 @@ func orderCreate(d *schema.ResourceData, meta interface{}, product string) error
}

// apply configurations
nbOfConfigurations := d.Get("plan.0.configuration.#").(int)
for i := 0; i < nbOfConfigurations; i++ {
configs := cartPlanParams.Configuration.Elements()

for _, cfg := range configs {
log.Printf("[DEBUG] Will create order cart item configuration for cart item: %s/%d",
item.CartId,
item.ItemId,
)
itemConfig := &OrderCartItemConfiguration{}
itemConfigParams := (&OrderCartItemConfigurationOpts{}).FromResourceWithPath(
d,
fmt.Sprintf("plan.0.configuration.%d", i),
)
endpoint := fmt.Sprintf("/order/cart/%s/item/%d/configuration",
url.PathEscape(item.CartId),
item.ItemId,
)
if err := config.OVHClient.Post(endpoint, itemConfigParams, itemConfig); err != nil {
return fmt.Errorf("calling Post %s with params %v:\n\t %q", endpoint, itemConfigParams, err)
if err := config.OVHClient.Post(endpoint, cfg, itemConfig); err != nil {
return fmt.Errorf("calling Post %s with params %v:\n\t %q", endpoint, cfg, err)
}
}

planOptionValue := d.PlanOption.Elements()

// Create Product Options Items
nbOfOptions := d.Get("plan_option.#").(int)
for i := 0; i < nbOfOptions; i++ {
optionPath := fmt.Sprintf("plan_option.%d", i)
for _, option := range planOptionValue {
opt := option.(PlanOptionValue)

log.Printf("[DEBUG] Will create order item options %s for cart: %s", product, cart.CartId)
productOptionsItem := &OrderCartItem{}
cartPlanOptionsParams := (&OrderCartPlanOptionsCreateOpts{
ItemId: item.ItemId,
Quantity: 1,
}).FromResourceWithPath(
d,
optionPath,
)

opt.ItemId = types.TfInt64Value{Int64Value: basetypes.NewInt64Value(item.ItemId)}
opt.Quantity = types.TfInt64Value{Int64Value: basetypes.NewInt64Value(1)}

endpoint := fmt.Sprintf("/order/cart/%s/%s/options", url.PathEscape(cart.CartId), product)
if err := config.OVHClient.Post(endpoint, cartPlanOptionsParams, productOptionsItem); err != nil {
if err := config.OVHClient.Post(endpoint, opt, productOptionsItem); err != nil {
return fmt.Errorf("calling Post %s with params %v:\n\t %q", endpoint, cartPlanParams, err)
}

// apply configurations
nbOfConfigurations := d.Get(fmt.Sprintf("%s.configuration.#", optionPath)).(int)
for j := 0; j < nbOfConfigurations; j++ {
optionConfigs := opt.Configuration.Elements()
for _, cfg := range optionConfigs {
log.Printf("[DEBUG] Will create order cart item configuration for cart item: %s/%d",
item.CartId,
item.ItemId,
)
itemConfig := &OrderCartItemConfiguration{}
itemConfigParams := (&OrderCartItemConfigurationOpts{}).FromResourceWithPath(
d,
fmt.Sprintf("%s.configuration.%d", optionPath, j),
)
endpoint := fmt.Sprintf("/order/cart/%s/item/%d/configuration",
url.PathEscape(item.CartId),
item.ItemId,
)
if err := config.OVHClient.Post(endpoint, itemConfigParams, itemConfig); err != nil {
return fmt.Errorf("calling Post %s with params %v:\n\t %q", endpoint, itemConfigParams, err)
if err := config.OVHClient.Post(endpoint, cfg, itemConfig); err != nil {
return fmt.Errorf("calling Post %s with params %v:\n\t %q", endpoint, cfg, err)
}
}
}
Expand Down Expand Up @@ -384,15 +390,33 @@ func orderCreate(d *schema.ResourceData, meta interface{}, product string) error
return fmt.Errorf("waiting for order (%d): %s", checkout.OrderID, err)
}

d.SetId(fmt.Sprint(checkout.OrderID))
d.Order.OrderId = types.TfInt64Value{Int64Value: basetypes.NewInt64Value(checkout.OrderID)}

return nil
}

func orderRead(d *schema.ResourceData, meta interface{}) (*MeOrder, []*MeOrderDetail, error) {
func orderReadInResource(d *schema.ResourceData, meta interface{}) (*MeOrder, []*MeOrderDetail, error) {
config := meta.(*Config)
orderId := d.Id()

order, details, err := orderRead(orderId, config)
if err != nil {
return nil, nil, err
}

detailsData := make([]map[string]interface{}, len(details))
for i, detail := range details {
detailsData[i] = detail.ToMap()
}

orderData := order.ToMap()
orderData["details"] = detailsData
d.Set("order", []interface{}{orderData})

return order, details, nil
}

func orderRead(orderId string, config *Config) (*MeOrder, []*MeOrderDetail, error) {
order := &MeOrder{}
log.Printf("[DEBUG] Will read order %s", orderId)
endpoint := fmt.Sprintf("/me/order/%s",
Expand All @@ -408,26 +432,31 @@ func orderRead(d *schema.ResourceData, meta interface{}) (*MeOrder, []*MeOrderDe
}

if len(details) < 1 {
return nil, nil, fmt.Errorf("There is no order details for id %s. This shouldn't happen. This is a bug with the API.", orderId)
return nil, nil, fmt.Errorf("there is no order details for id %s. This shouldn't happen. This is a bug with the API", orderId)
}

detailsData := make([]map[string]interface{}, len(details))
for i, detail := range details {
detailsData[i] = detail.ToMap()
}

orderData := order.ToMap()
orderData["details"] = detailsData
d.Set("order", []interface{}{orderData})

return order, details, nil
}

type TerminateFunc func() (string, error)
type ConfirmTerminationFunc func(token string) error

func orderDelete(d *schema.ResourceData, meta interface{}, terminate TerminateFunc, confirm ConfirmTerminationFunc) error {
oldEmailsIds, err := notificationEmailSortedIds(meta)
func orderDeleteFromResource(d *schema.ResourceData, meta interface{}, terminate TerminateFunc, confirm ConfirmTerminationFunc) error {
config := meta.(*Config)

if err := orderDelete(config, terminate, confirm); err != nil {
return err
}

if d != nil {
d.SetId("")
}

return nil
}

func orderDelete(config *Config, terminate TerminateFunc, confirm ConfirmTerminationFunc) error {
oldEmailsIds, err := notificationEmailSortedIds(config)
if err != nil {
return err
}
Expand All @@ -450,7 +479,7 @@ func orderDelete(d *schema.ResourceData, meta interface{}, terminate TerminateFu
var email *NotificationEmail
// wait for email
err = resource.Retry(30*time.Minute, func() *resource.RetryError {
email, err = getNewNotificationEmail(matches, oldEmailsIds, meta)
email, err = getNewNotificationEmail(matches, oldEmailsIds, config)
if err != nil {
log.Printf("[DEBUG] error while getting email notification. retry: %v", err)
return resource.RetryableError(err)
Expand Down Expand Up @@ -478,10 +507,6 @@ func orderDelete(d *schema.ResourceData, meta interface{}, terminate TerminateFu
return err
}

if d != nil {
d.SetId("")
}

return nil
}

Expand All @@ -507,6 +532,38 @@ func orderDetails(c *ovh.Client, orderId int64) ([]*MeOrderDetail, error) {
return details, nil
}

func serviceNameFromOrder(c *ovh.Client, orderId int64, plan string) (string, error) {
detailIds := []int64{}
endpoint := fmt.Sprintf("/me/order/%d/details", orderId)
if err := c.Get(endpoint, &detailIds); err != nil {
return "", fmt.Errorf("calling get %s:\n\t %q", endpoint, err)
}

for _, detailId := range detailIds {
detailExtension := &MeOrderDetailExtension{}
log.Printf("[DEBUG] Will read order detail extension %d/%d", orderId, detailId)
endpoint := fmt.Sprintf("/me/order/%d/details/%d/extension", orderId, detailId)
if err := c.Get(endpoint, detailExtension); err != nil {
return "", fmt.Errorf("calling get %s:\n\t %q", endpoint, err)
}

if detailExtension.Order.Plan.Code != plan {
continue
}

detail := &MeOrderDetail{}
log.Printf("[DEBUG] Will read order detail %d/%d", orderId, detailId)
endpoint = fmt.Sprintf("/me/order/%d/details/%d", orderId, detailId)
if err := c.Get(endpoint, detail); err != nil {
return "", fmt.Errorf("calling get %s:\n\t %q", endpoint, err)
}

return detail.Domain, nil
}

return "", errors.New("serviceName not found")
}

func waitForOrder(c *ovh.Client, id int64) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var r string
Expand Down
Loading