diff --git a/.changelog/544.txt b/.changelog/544.txt deleted file mode 100644 index 24f406b0..00000000 --- a/.changelog/544.txt +++ /dev/null @@ -1,3 +0,0 @@ -```release-note:feature -`resource/cloudavenue_backup` - New resource to manage NetBackup feature. -``` \ No newline at end of file diff --git a/.changelog/558.txt b/.changelog/558.txt new file mode 100644 index 00000000..264eed10 --- /dev/null +++ b/.changelog/558.txt @@ -0,0 +1,7 @@ +```release-note:feature +`resource/cloudavenue_backup` - New resource to manage NetBackup feature. +``` + +```release-note:feature +`datasource/cloudavenue_backup` - New datasource to manage NetBackup feature. +``` \ No newline at end of file diff --git a/docs/data-sources/backup.md b/docs/data-sources/backup.md new file mode 100644 index 00000000..b54f4ddf --- /dev/null +++ b/docs/data-sources/backup.md @@ -0,0 +1,45 @@ +--- +page_title: "cloudavenue_backup Data Source - cloudavenue" +subcategory: "Backup" +description: |- + The cloudavenue_backup data source allows you to retrieve information about a backup of NetBackup solution. +--- + +# cloudavenue_backup (Data Source) + +The `cloudavenue_backup` data source allows you to retrieve information about a backup of NetBackup solution. + +## Example Usage + +```terraform +data "cloudavenue_backup" "example" { + type = "vdc" + target_name = data.cloudavenue_vdc.example.name +} +``` + + +## Schema + +### Required + +- `type` (String) Scope of the backup. + +### Optional + +- `id` (Number) The ID of the backup. +- `target_id` (String) The ID of the target. A target can be a VDC, a VApp or a VM. +- `target_name` (String) The name of the target. A target can be a VDC, a VApp or a VM. + +### Read-Only + +- `policies` (Attributes Set) The backup policies of the target. (see [below for nested schema](#nestedatt--policies)) + + +### Nested Schema for `policies` + +Read-Only: + +- `policy_id` (Number) The ID of the backup policy. +- `policy_name` (String) The name of the backup policy. + diff --git a/examples/data-sources/cloudavenue_backup/data-source.tf b/examples/data-sources/cloudavenue_backup/data-source.tf new file mode 100644 index 00000000..20de5f19 --- /dev/null +++ b/examples/data-sources/cloudavenue_backup/data-source.tf @@ -0,0 +1,4 @@ +data "cloudavenue_backup" "example" { + type = "vdc" + target_name = data.cloudavenue_vdc.example.name +} \ No newline at end of file diff --git a/go.mod b/go.mod index d6f758b7..bcf05e45 100644 --- a/go.mod +++ b/go.mod @@ -19,12 +19,12 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 github.com/iancoleman/strcase v0.3.0 + github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.3-0.20231009201953-47ea587cab76 github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb - github.com/orange-cloudavenue/netbackup-sdk-go v0.0.2-0.20230928095122-108fd1ec28ae github.com/rs/zerolog v1.31.0 github.com/thanhpk/randstr v1.0.6 github.com/vmware/go-vcloud-director/v2 v2.21.0 - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df + golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/net v0.16.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -48,7 +48,7 @@ require ( github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-chi/render v1.0.3 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-resty/resty/v2 v2.8.0 // indirect + github.com/go-resty/resty/v2 v2.9.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -88,6 +88,7 @@ require ( github.com/posener/complete v1.2.3 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/russross/blackfriday v1.6.0 // indirect + github.com/sethvargo/go-envconfig v0.9.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect @@ -95,7 +96,7 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.0 // indirect golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.13.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect @@ -104,5 +105,3 @@ require ( google.golang.org/grpc v1.57.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) - -replace github.com/orange-cloudavenue/netbackup-sdk-go => /Users/micheneaudavid/go/src/github.com/orange-cloudavenue/netbackup-sdk-go diff --git a/go.sum b/go.sum index 7a455e2f..7f820666 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-resty/resty/v2 v2.8.0 h1:J29d0JFWwSWrDCysnOK/YjsPMLQTx0TvgJEHVGvf2L8= -github.com/go-resty/resty/v2 v2.8.0/go.mod h1:UCui0cMHekLrSntoMyofdSTaPpinlRHFtPpizuyDW2w= +github.com/go-resty/resty/v2 v2.9.1 h1:PIgGx4VrHvag0juCJ4dDv3MiFRlDmP0vicBucwf+gLM= +github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -216,6 +216,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.3-0.20231009201953-47ea587cab76 h1:wMZjH50aktG4x7/B6LWF49vhdI/0Oj5/aWy6nu5EBHk= +github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.3-0.20231009201953-47ea587cab76/go.mod h1:DWBIS3DJtS5ZiZzblCwYNo123RaxO+UrGyfqsVQbFb0= github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb h1:1/Wc21Tp9RnDOUTjKBm9x3wi+UgUkDc2bv0fHJc5f2o= github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb/go.mod h1:pGa9mB6s+weCi5QtNe5nicp7yL0C/e+i+3wHRh4cjBE= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= @@ -240,6 +242,8 @@ github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3V github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE= +github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -286,12 +290,12 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -359,12 +363,14 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/internal/client/client.go b/internal/client/client.go index 365f628f..ec330cf6 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -9,6 +9,7 @@ import ( "github.com/vmware/go-vcloud-director/v2/govcd" + clientca "github.com/orange-cloudavenue/cloudavenue-sdk-go" apiclient "github.com/orange-cloudavenue/infrapi-sdk-go" ) @@ -44,7 +45,9 @@ type CloudAvenue struct { VCDVersion string // API NetBackup - NetBackup *NetBackup + // NetBackup *NetBackup + NetBackupClient *clientca.Client + NetBackupOpts *clientca.ClientOpts } // New creates a new CloudAvenue client. @@ -77,11 +80,15 @@ func (c *CloudAvenue) New() (*CloudAvenue, error) { } // API NetBackup - if c.NetBackup.IsDefined() { - if err := c.NewNetBackupClient(); err != nil { - return nil, err - } + // if c.NetBackup.Netbackup.Endpoint != "" || c.NetBackup.Netbackup.Username != "" || c.NetBackup.Netbackup.Password != "" { + c.NetBackupClient, err = clientca.New(*c.NetBackupOpts) + if err != nil { + return nil, fmt.Errorf("%w : %w", ErrConfigureNetBackup, err) } + // if err := c.NewNetBackupClient(); err != nil { + // return nil, err + // } + // } return c, nil } diff --git a/internal/client/netbackup.go b/internal/client/netbackup.go deleted file mode 100644 index f3751ede..00000000 --- a/internal/client/netbackup.go +++ /dev/null @@ -1,37 +0,0 @@ -package client - -import ( - "fmt" - - netbackupclient "github.com/orange-cloudavenue/netbackup-sdk-go" -) - -// Netbackup is the main struct for the NetBackup client. -type NetBackup struct { - Client *netbackupclient.Client - URL string - User string - Password string -} - -// NewNetBackup creates a new NetBackup client. -func (c *CloudAvenue) NewNetBackupClient() (err error) { - c.NetBackup.Client, err = netbackupclient.New(netbackupclient.Opts{ - APIEndpoint: c.NetBackup.URL, - Username: c.NetBackup.User, - Password: c.NetBackup.Password, - Debug: false, - }) - if err != nil { - return fmt.Errorf("%w : %w", ErrConfigureNetBackup, err) - } - return -} - -// IsDefined checks if the NetBackup configuration is defined. -func (nB *NetBackup) IsDefined() bool { - if nB.URL != "" || nB.User != "" || nB.Password != "" { - return true - } - return false -} diff --git a/internal/provider/backup/backup_resource.go b/internal/provider/backup/backup_resource.go index 0e9e72b0..90bc8b3c 100644 --- a/internal/provider/backup/backup_resource.go +++ b/internal/provider/backup/backup_resource.go @@ -5,13 +5,12 @@ import ( "fmt" "strings" - "github.com/orange-cloudavenue/netbackup-sdk-go/netbackupclient" - "github.com/orange-cloudavenue/netbackup-sdk-go/netbackupclient/common" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource" + v1common "github.com/orange-cloudavenue/cloudavenue-sdk-go/pkg/common/netbackup" + v1 "github.com/orange-cloudavenue/cloudavenue-sdk-go/v1" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/client" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/metrics" ) @@ -84,6 +83,17 @@ func (r *backupResource) Create(ctx context.Context, req resource.CreateRequest, return } + // Refresh data NetBackup from the API + job, err := r.client.NetBackupClient.V1.Netbackup.Inventory.Refresh() + if err != nil { + resp.Diagnostics.AddError("Error refreshing NetBackup inventory", err.Error()) + return + } + if err := job.Wait(1, 45); err != nil { + resp.Diagnostics.AddError("Error waiting for NetBackup inventory refresh", err.Error()) + return + } + // Get the type target object typeTarget, d := r.getTarget(plan) if d.HasError() { @@ -93,7 +103,7 @@ func (r *backupResource) Create(ctx context.Context, req resource.CreateRequest, // Apply the protection levels policies for each policy if err := applyPolicies(typeTarget, policies); err != nil { - resp.Diagnostics.AddError("Error applying VDC protection levels", err.Error()) + resp.Diagnostics.AddError("Error applying protection levels", err.Error()) return } @@ -281,6 +291,17 @@ func (r *backupResource) ImportState(ctx context.Context, req resource.ImportSta return } + // Refresh data NetBackup from the API + job, err := r.client.NetBackupClient.V1.Netbackup.Inventory.Refresh() + if err != nil { + resp.Diagnostics.AddError("Error refreshing NetBackup inventory", err.Error()) + return + } + if err := job.Wait(1, 45); err != nil { + resp.Diagnostics.AddError("Error waiting for NetBackup inventory refresh", err.Error()) + return + } + data := NewBackup() data.Type.Set(idParts[0]) data.TargetName.Set(idParts[1]) @@ -299,11 +320,11 @@ func (r *backupResource) ImportState(ctx context.Context, req resource.ImportSta // * CustomFuncs type target interface { - GetProtectionLevelAvailableByName(string) (*netbackupclient.ProtectionLevel, error) - Protect(netbackupclient.ProtectUnprotectRequest) (*common.JobAPIResponse, error) - Unprotect(netbackupclient.ProtectUnprotectRequest) (*common.JobAPIResponse, error) + GetProtectionLevelAvailableByName(string) (*v1.ProtectionLevel, error) + Protect(v1.ProtectUnprotectRequest) (*v1common.JobAPIResponse, error) + Unprotect(v1.ProtectUnprotectRequest) (*v1common.JobAPIResponse, error) GetID() int - ListProtectionLevels() (*netbackupclient.ProtectionLevels, error) + ListProtectionLevels() (*v1.ProtectionLevels, error) } // Apply the protection level for a policy to the target. @@ -311,11 +332,8 @@ type target interface { // Return a policy with the protection level ID. // Return an error if any. func applyPolicy[T target](t T, policy backupModelPolicy) (backupModelPolicy, error) { - var job *common.JobAPIResponse - var err error - // apply the protection levels - job, err = t.Protect(netbackupclient.ProtectUnprotectRequest{ + job, err := t.Protect(v1.ProtectUnprotectRequest{ ProtectionLevelID: policy.PolicyID.GetIntPtr(), ProtectionLevelName: policy.PolicyName.Get(), }) @@ -356,8 +374,7 @@ func applyPolicies[T target](t T, policies *backupModelPolicies) (err error) { // Return an error if any. func unApplyPolicies[T target](t T, policies *backupModelPolicies) (err error) { for _, policy := range *policies { - err := unApplyPolicy(t, policy) - if err != nil { + if err := unApplyPolicy(t, policy); err != nil { return err } } @@ -369,7 +386,7 @@ func unApplyPolicies[T target](t T, policies *backupModelPolicies) (err error) { // Return an error if any. func unApplyPolicy[T target](t T, policy backupModelPolicy) error { // apply the protection levels - job, err := t.Unprotect(netbackupclient.ProtectUnprotectRequest{ + job, err := t.Unprotect(v1.ProtectUnprotectRequest{ ProtectionLevelID: policy.PolicyID.GetIntPtr(), ProtectionLevelName: policy.PolicyName.Get(), }) @@ -387,7 +404,7 @@ func unApplyPolicy[T target](t T, policy backupModelPolicy) error { func (r *backupResource) read(ctx context.Context, planOrState *backupModel) (stateRefreshed *backupModel, found bool, diags diag.Diagnostics) { stateRefreshed = planOrState.Copy() - var policiesFromAPI *netbackupclient.ProtectionLevels + var policiesFromAPI *v1.ProtectionLevels // *netbackupclient.ProtectionLevels // Get the type target object typeTarget, d := r.getTarget(planOrState) @@ -433,11 +450,11 @@ func (r *backupResource) getTarget(data *backupModel) (typeTarget target, d diag var err error switch data.Type.Get() { case vdc: - typeTarget, err = r.client.NetBackup.Client.VCloud.GetVdcByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.NetBackupClient.V1.Netbackup.VCloud.GetVdcByNameOrIdentifier(data.getTargetIDOrName()) case vapp: - typeTarget, err = r.client.NetBackup.Client.VCloud.GetVAppByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.NetBackupClient.V1.Netbackup.VCloud.GetVAppByNameOrIdentifier(data.getTargetIDOrName()) case vm: - typeTarget, err = r.client.NetBackup.Client.Machines.GetMachineByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.NetBackupClient.V1.Netbackup.Machines.GetMachineByNameOrIdentifier(data.getTargetIDOrName()) } if err != nil { d.AddError(fmt.Sprintf("Error getting vCloud Director %s", data.Type.Get()), err.Error()) diff --git a/internal/provider/backup/backup_schema.go b/internal/provider/backup/backup_schema.go index 464b3ff1..61d8386b 100644 --- a/internal/provider/backup/backup_schema.go +++ b/internal/provider/backup/backup_schema.go @@ -24,7 +24,7 @@ import ( func backupSchema(_ context.Context) superschema.Schema { return superschema.Schema{ Resource: superschema.SchemaDetails{ - MarkdownDescription: "The `cloudavenue_backup` resource allows you to manage backup strategy for `vdc`,`vapp` and 'vm' from NetBackup solution. Please refer to the documentation for more information. https://wiki.cloudavenue.orange-business.com/wiki/Backup", + MarkdownDescription: "The `cloudavenue_backup` resource allows you to manage backup strategy for `vdc`, `vapp` and `vm` from NetBackup solution. [Please refer to the documentation for more information.](https://wiki.cloudavenue.orange-business.com/wiki/Backup)", }, DataSource: superschema.SchemaDetails{ MarkdownDescription: "The `cloudavenue_backup` data source allows you to retrieve information about a backup of NetBackup solution.", @@ -117,14 +117,13 @@ func backupSchema(_ context.Context) superschema.Schema { }, "policy_name": superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ - MarkdownDescription: "The name of the backup policy.", + MarkdownDescription: "The name of the backup policy. Each letter represent a strategy predefined: D = Daily, W = Weekly, M = Monthly, X = Replication, The number is the retention period. [Please refer to the documentation for more information.](https://wiki.cloudavenue.orange-business.com/wiki/Backup)", }, Resource: &schemaR.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.OneOf("D6", "D30", "D30NQ", "D60", "W4", "M3", "M12", "XD6", "XD30", "XD60", "XW4", "XM3", "XM12"), }, - MarkdownDescription: "Each letter represent a strategy predefined: D = Daily, W = Weekly, M = Monthly, X = Replication, The number is the retention period. Please refer to the documentation for more information. https://wiki.cloudavenue.orange-business.com/wiki/Backup", }, DataSource: &schemaD.StringAttribute{ Computed: true, diff --git a/internal/provider/backup/backup_schema_test.go b/internal/provider/backup/backup_schema_test.go index d639121f..e7e6ba14 100644 --- a/internal/provider/backup/backup_schema_test.go +++ b/internal/provider/backup/backup_schema_test.go @@ -7,6 +7,7 @@ import ( // The fwresource import alias is so there is no collistion // with the more typical acceptance testing import: // "github.com/hashicorp/terraform-plugin-testing/helper/resource". + fwdatasource "github.com/hashicorp/terraform-plugin-framework/datasource" fwresource "github.com/hashicorp/terraform-plugin-framework/resource" netbackup "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/backup" @@ -36,16 +37,16 @@ func TestBackupResourceSchema(t *testing.T) { } } -// Unit test for the schema of the datasource cloudavenue_netbackup_Backup -/* +// Unit test for the schema of the datasource cloudavenue_backup + func TestBackupDataSourceSchema(t *testing.T) { t.Parallel() ctx := context.Background() - schemaResponse := &fwresource.SchemaResponse{} + schemaResponse := &fwdatasource.SchemaResponse{} // Instantiate the datasource.Datasource and call its Schema method - netbackup.NewBackupDataSource().Schema(ctx, fwresource.SchemaRequest{}, schemaResponse) + netbackup.NewBackupDataSource().Schema(ctx, fwdatasource.SchemaRequest{}, schemaResponse) if schemaResponse.Diagnostics.HasError() { t.Fatalf("Schema method diagnostics: %+v", schemaResponse.Diagnostics) @@ -58,4 +59,3 @@ func TestBackupDataSourceSchema(t *testing.T) { t.Fatalf("Schema validation diagnostics: %+v", diagnostics) } } -*/ diff --git a/internal/provider/provider.go b/internal/provider/provider.go index c63e1cc5..c99eee8d 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -10,6 +10,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + casdk "github.com/orange-cloudavenue/cloudavenue-sdk-go" + clientcloudavenue "github.com/orange-cloudavenue/cloudavenue-sdk-go/pkg/clients/cloudavenue" + clientnetbackup "github.com/orange-cloudavenue/cloudavenue-sdk-go/pkg/clients/netbackup" + "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/client" ) @@ -54,6 +58,12 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config return } + netbackup := clientnetbackup.Opts{ + Endpoint: findValue(config.NetBackupURL, "NETBACKUP_URL"), + Username: findValue(config.NetBackupUser, "NETBACKUP_USER"), + Password: findValue(config.NetBackupPassword, "NETBACKUP_PASSWORD"), + } + cloudAvenue := client.CloudAvenue{ URL: func() string { url := findValue(config.URL, "CLOUDAVENUE_URL") @@ -69,11 +79,7 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config TerraformVersion: req.TerraformVersion, CloudAvenueVersion: p.version, VCDVersion: VCDVersion, - NetBackup: &client.NetBackup{ - URL: findValue(config.NetBackupURL, "NETBACKUP_URL"), - User: findValue(config.NetBackupUser, "NETBACKUP_USER"), - Password: findValue(config.NetBackupPassword, "NETBACKUP_PASSWORD"), - }, + NetBackupOpts: &casdk.ClientOpts{Netbackup: netbackup, CloudAvenue: clientcloudavenue.Opts{}}, } // If any of the expected configurations are missing, return @@ -106,30 +112,30 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config ) } - if cloudAvenue.NetBackup.IsDefined() { - // Default URL to the public NetBackup API if not set. - if cloudAvenue.NetBackup.URL == "" { - cloudAvenue.NetBackup.URL = "https://backup1.cloudavenue.orange-business.com/NetBackupSelfServiceNetBackupPanels/Api" - } - if cloudAvenue.NetBackup.User == "" { - resp.Diagnostics.AddAttributeError( - path.Root("netbackup_user"), - "Missing NetBackup API User", - "The provider cannot create the NetBackup API client as there is a missing or empty value for the NetBackup API user. "+ - "Set the host value in the configuration or use the NETBACKUP_USER environment variable. "+ - "If either is already set, ensure the value is not empty.", - ) - } - if cloudAvenue.NetBackup.Password == "" { - resp.Diagnostics.AddAttributeError( - path.Root("netbackup_password"), - "Missing NetBackup API Password", - "The provider cannot create the NetBackup API client as there is a missing or empty value for the NetBackup API password. "+ - "Set the host value in the configuration or use the NETBACKUP_PASSWORD environment variable. "+ - "If either is already set, ensure the value is not empty.", - ) - } - } + // if cloudAvenue.NetBackupOpts.IsDefined() { + // // Default URL to the public NetBackup API if not set. + // if cloudAvenue.NetBackupOpts.URL == "" { + // cloudAvenue.NetBackupOpts.URL = "https://backup1.cloudavenue.orange-business.com/NetBackupSelfServiceNetBackupPanels/Api" + // } + // if cloudAvenue.NetBackup.User == "" { + // resp.Diagnostics.AddAttributeError( + // path.Root("netbackup_user"), + // "Missing NetBackup API User", + // "The provider cannot create the NetBackup API client as there is a missing or empty value for the NetBackup API user. "+ + // "Set the host value in the configuration or use the NETBACKUP_USER environment variable. "+ + // "If either is already set, ensure the value is not empty.", + // ) + // } + // if cloudAvenue.NetBackup.Password == "" { + // resp.Diagnostics.AddAttributeError( + // path.Root("netbackup_password"), + // "Missing NetBackup API Password", + // "The provider cannot create the NetBackup API client as there is a missing or empty value for the NetBackup API password. "+ + // "Set the host value in the configuration or use the NETBACKUP_PASSWORD environment variable. "+ + // "If either is already set, ensure the value is not empty.", + // ) + // } + // } if resp.Diagnostics.HasError() { return } diff --git a/internal/provider/provider_datasources.go b/internal/provider/provider_datasources.go index 29aee1c2..8c8f6f83 100644 --- a/internal/provider/provider_datasources.go +++ b/internal/provider/provider_datasources.go @@ -6,7 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/alb" - netbackup "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/backup" + "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/backup" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/catalog" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/edgegw" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/iam" @@ -82,6 +82,6 @@ func (p *cloudavenueProvider) DataSources(_ context.Context) []func() datasource storage.NewProfilesDataSource, // * BACKUP - netbackup.NewBackupDataSource, + backup.NewBackupDataSource, } } diff --git a/internal/testsacc/backup_datasource_test.go b/internal/testsacc/backup_datasource_test.go index 4a12138f..d162b8c8 100644 --- a/internal/testsacc/backup_datasource_test.go +++ b/internal/testsacc/backup_datasource_test.go @@ -36,7 +36,7 @@ func (r *BackupDataSource) DependenciesConfig() (configs testsacc.TFData) { func (r *BackupDataSource) Tests(ctx context.Context) map[testsacc.TestName]func(ctx context.Context, resourceName string) testsacc.Test { return map[testsacc.TestName]func(ctx context.Context, resourceName string) testsacc.Test{ // * Test One (backup vdc example) - "example": func(_ context.Context, resourceName string) testsacc.Test { + "example": func(_ context.Context, _ string) testsacc.Test { return testsacc.Test{ // ! Create testing Create: testsacc.TFConfig{ @@ -45,7 +45,21 @@ func (r *BackupDataSource) Tests(ctx context.Context) map[testsacc.TestName]func type = "vdc" target_name = cloudavenue_backup.example.target_name }`, - Checks: NewBackupResourceTest().Tests(ctx)["example"](ctx, resourceName).GenerateCheckWithCommonChecks(), + Checks: GetResourceConfig()[BackupResourceName]().GetDefaultChecks(), + }, + } + }, + // * Test Two (datasource with vdc ID example) + "example_2": func(_ context.Context, _ string) testsacc.Test { + return testsacc.Test{ + // ! Create testing + Create: testsacc.TFConfig{ + TFConfig: ` + data "cloudavenue_backup" "example" { + type = "vdc" + target_id = cloudavenue_backup.example.target_id + }`, + Checks: GetResourceConfig()[BackupResourceName]().GetDefaultChecks(), }, } }, diff --git a/internal/testsacc/backup_resource_test.go b/internal/testsacc/backup_resource_test.go index 86d85448..d0e5ae74 100644 --- a/internal/testsacc/backup_resource_test.go +++ b/internal/testsacc/backup_resource_test.go @@ -2,11 +2,9 @@ package testsacc import ( "context" - "fmt" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/helpers/testsacc" ) @@ -31,7 +29,7 @@ func (r *BackupResource) GetResourceName() string { func (r *BackupResource) DependenciesConfig() (configs testsacc.TFData) { // TODO : Add dependencies config configs.Append(GetResourceConfig()[VDCResourceName]().GetDefaultConfig()) - // configs.Append(GetResourceConfig()[VAPPResourceName]().GetDefaultConfig()) + configs.Append(GetResourceConfig()[VAppResourceName]().GetDefaultConfig()) // configs.Append(GetResourceConfig()[VMResourceName]().GetDefaultConfig()) return } @@ -40,70 +38,71 @@ func (r *BackupResource) Tests(ctx context.Context) map[testsacc.TestName]func(c return map[testsacc.TestName]func(ctx context.Context, resourceName string) testsacc.Test{ // TODO : Complete tests // * First Test For a VDC Backup named "example" - "example": func(_ context.Context, resourceName string) testsacc.Test { - return testsacc.Test{ - CommonChecks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttrSet(resourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "target_name", "example"), - }, - // ! Create testing - Create: testsacc.TFConfig{ - TFConfig: ` - resource "cloudavenue_backup" "example" { - type = "vdc" - target_name = cloudavenue_vdc.example.name - policies = [{ - policy_name = "D6" - }] - }`, - Checks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttr(resourceName, "type", "vdc"), - resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), - }, - }, - // ! Updates testing - Updates: []testsacc.TFConfig{ - { - TFConfig: ` - resource "cloudavenue_backup" "example" { - type = "vdc" - target_name = cloudavenue_vdc.example.name - policies = [{ - policy_name = "D6" - }, - { - policy_name = "D30" - }] - }`, - Checks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), - resource.TestCheckResourceAttr(resourceName, "policies.1.policy_name", "D30"), - }, - }, - }, - // ! Imports testing - Imports: []testsacc.TFImport{ - { - ImportStateIDFunc: testAccBackupResourceImportStateIDFuncWithTypeAndTargetName(resourceName), - ImportState: true, - ImportStateVerify: true, - }, - }, - } - }, + // "example": func(_ context.Context, resourceName string) testsacc.Test { + // // tflog.Info(ctx, pp.Sprintf("GetAllValuesFromTemplate: %v", testsacc.GetAllValuesFromTemplate())) + // return testsacc.Test{ + // CommonChecks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttrSet(resourceName, "id"), + // resource.TestCheckResourceAttrSet(resourceName, "target_name"), + // }, + // // ! Create testing + // Create: testsacc.TFConfig{ + // TFConfig: ` + // resource "cloudavenue_backup" "example" { + // type = "vdc" + // target_name = cloudavenue_vdc.example.name + // policies = [{ + // policy_name = "D6" + // }] + // }`, + // Checks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttr(resourceName, "type", "vdc"), + // resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), + // }, + // }, + // // ! Updates testing + // Updates: []testsacc.TFConfig{ + // { + // TFConfig: ` + // resource "cloudavenue_backup" "example" { + // type = "vdc" + // target_name = cloudavenue_vdc.example.name + // policies = [{ + // policy_name = "D6" + // },{ + // policy_name = "D30" + // } + // ] + // }`, + // Checks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), + // resource.TestCheckResourceAttr(resourceName, "policies.1.policy_name", "D30"), + // }, + // }, + // }, + // // ! Imports testing + // Imports: []testsacc.TFImport{ + // { + // ImportStateIDBuilder: []string{"type", "target_name"}, + // ImportState: true, + // ImportStateVerify: true, + // }, + // }, + // } + // }, // * Second Test For a VAPP Backup named "example" - "example2": func(_ context.Context, resourceName string) testsacc.Test { + "example_2": func(_ context.Context, resourceName string) testsacc.Test { return testsacc.Test{ CommonChecks: []resource.TestCheckFunc{ resource.TestCheckResourceAttrSet(resourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "target_name", "example"), + resource.TestCheckResourceAttrSet(resourceName, "target_id"), }, // ! Create testing Create: testsacc.TFConfig{ TFConfig: ` - resource "cloudavenue_backup" "example" { + resource "cloudavenue_backup" "example_2" { type = "vapp" - target_name = cloudavenue_vapp.example.name + target_id = cloudavenue_vapp.example.id policies = [{ policy_name = "D6" }] @@ -117,61 +116,9 @@ func (r *BackupResource) Tests(ctx context.Context) map[testsacc.TestName]func(c Updates: []testsacc.TFConfig{ { TFConfig: ` - resource "cloudavenue_backup" "example" { + resource "cloudavenue_backup" "example_2" { type = "vapp" - target_name = cloudavenue_vapp.example.name - policies = [{ - policy_name = "D6" - }, - { - policy_name = "D30" - }] - }`, - Checks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), - resource.TestCheckResourceAttr(resourceName, "policies.1.policy_name", "D30"), - }, - }, - }, - // ! Imports testing - Imports: []testsacc.TFImport{ - { - ImportStateIDFunc: testAccBackupResourceImportStateIDFuncWithTypeAndTargetName(resourceName), - ImportState: true, - ImportStateVerify: true, - }, - }, - } - }, - // * Second Test For a VM Backup named "example" - "example3": func(_ context.Context, resourceName string) testsacc.Test { - return testsacc.Test{ - CommonChecks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttrSet(resourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "target_name", "example"), - }, - // ! Create testing - Create: testsacc.TFConfig{ - TFConfig: ` - resource "cloudavenue_backup" "example" { - type = "vm" - target_name = cloudavenue_vm.example.name - policies = [{ - policy_name = "D6" - }] - }`, - Checks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttr(resourceName, "type", "vm"), - resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), - }, - }, - // ! Updates testing - Updates: []testsacc.TFConfig{ - { - TFConfig: ` - resource "cloudavenue_backup" "example" { - type = "vm" - target_name = cloudavenue_vm.example.name + target_id = cloudavenue_vapp.example.id policies = [{ policy_name = "D6" }, @@ -188,25 +135,65 @@ func (r *BackupResource) Tests(ctx context.Context) map[testsacc.TestName]func(c // ! Imports testing Imports: []testsacc.TFImport{ { - ImportStateIDFunc: testAccBackupResourceImportStateIDFuncWithTypeAndTargetName(resourceName), - ImportState: true, - ImportStateVerify: true, + ImportStateIDBuilder: []string{"type", "target_name"}, + ImportState: true, + ImportStateVerify: true, }, }, } }, - } -} - -func testAccBackupResourceImportStateIDFuncWithTypeAndTargetName(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not found: %s", resourceName) - } - - // Type.Target_name - return fmt.Sprintf("%s.%s", rs.Primary.Attributes["type"], rs.Primary.Attributes["target_name"]), nil + // // * Second Test For a VM Backup named "example" + // "example3": func(_ context.Context, resourceName string) testsacc.Test { + // return testsacc.Test{ + // CommonChecks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttrSet(resourceName, "id"), + // resource.TestCheckResourceAttrSet(resourceName, "target_name"), + // }, + // // ! Create testing + // Create: testsacc.TFConfig{ + // TFConfig: ` + // resource "cloudavenue_backup" "example" { + // type = "vm" + // target_name = cloudavenue_vm.example.name + // policies = [{ + // policy_name = "D6" + // }] + // }`, + // Checks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttr(resourceName, "type", "vm"), + // resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), + // }, + // }, + // // ! Updates testing + // Updates: []testsacc.TFConfig{ + // { + // TFConfig: ` + // resource "cloudavenue_backup" "example" { + // type = "vm" + // target_name = cloudavenue_vm.example.name + // policies = [{ + // policy_name = "D6" + // }, + // { + // policy_name = "D30" + // }] + // }`, + // Checks: []resource.TestCheckFunc{ + // resource.TestCheckResourceAttr(resourceName, "policies.0.policy_name", "D6"), + // resource.TestCheckResourceAttr(resourceName, "policies.1.policy_name", "D30"), + // }, + // }, + // }, + // // ! Imports testing + // Imports: []testsacc.TFImport{ + // { + // ImportStateIDFunc: testAccBackupResourceImportStateIDFuncWithTypeAndTargetName(resourceName), + // ImportState: true, + // ImportStateVerify: true, + // }, + // }, + // } + // }, } } diff --git a/templates/data-sources/backup.md.tmpl b/templates/data-sources/backup.md.tmpl new file mode 100644 index 00000000..d5075e3c --- /dev/null +++ b/templates/data-sources/backup.md.tmpl @@ -0,0 +1,25 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "Backup" +description: |- +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +--- + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile .ExampleFile }} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} + +{{ if .HasImport -}} +## Import + +Import is supported using the following syntax: +{{ codefile "shell" .ImportFile }} +{{- end }} \ No newline at end of file