From c8e6c3a1e06cae57a562a242d49c6fc4dad14300 Mon Sep 17 00:00:00 2001 From: David MICHENEAU Date: Wed, 27 Sep 2023 23:35:19 +0200 Subject: [PATCH] feat: Add Netbackup client available --- .changelog/546.txt | 3 ++ docs/index.md | 3 ++ go.mod | 2 ++ go.sum | 9 +++++ internal/client/client.go | 23 ++++++++++++ internal/provider/provider.go | 53 ++++++++++++++++++++++++++-- internal/provider/provider_schema.go | 19 ++++++++++ internal/provider/provider_types.go | 13 ++++--- 8 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 .changelog/546.txt diff --git a/.changelog/546.txt b/.changelog/546.txt new file mode 100644 index 00000000..c049502e --- /dev/null +++ b/.changelog/546.txt @@ -0,0 +1,3 @@ +```release-note:feature +`client/cloudavenue` - Add `NetBackup` credentials in provider configuration. +``` diff --git a/docs/index.md b/docs/index.md index 1f33e39a..ffc07112 100644 --- a/docs/index.md +++ b/docs/index.md @@ -35,6 +35,9 @@ provider "cloudavenue" { ### Optional +- `netbackup_password` (String, Sensitive) The password to use to connect to the NetBackup API. Can also be set with the `NETBACKUP_PASSWORD` environment variable. +- `netbackup_url` (String) The URL of the NetBackup API. Can also be set with the `NETBACKUP_URL` environment variable. +- `netbackup_user` (String) The username to use to connect to the NetBackup API. Can also be set with the `NETBACKUP_USER` environment variable. - `org` (String) The organization used on Cloud Avenue API. Can also be set with the `CLOUDAVENUE_ORG` environment variable. - `password` (String, Sensitive) The password to use to connect to the Cloud Avenue API. Can also be set with the `CLOUDAVENUE_PASSWORD` environment variable. - `url` (String) The URL of the Cloud Avenue API. Can also be set with the `CLOUDAVENUE_URL` environment variable. diff --git a/go.mod b/go.mod index 58ec3b9c..ed553dea 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( 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.1.3 + github.com/orange-cloudavenue/netbackup-sdk-go v0.0.2-0.20230928095122-108fd1ec28ae github.com/rs/zerolog v1.30.0 github.com/thanhpk/randstr v1.0.6 github.com/vmware/go-vcloud-director/v2 v2.21.0 @@ -47,6 +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/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index e0ff8905..9fded712 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +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-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 +218,8 @@ 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.1.3 h1:nnFO5vxf70gWZkPcM153LYwHA1fa+0QqtzH2t71AMFA= github.com/orange-cloudavenue/cloudavenue-sdk-go v0.1.3/go.mod h1:bMSQ+EQ0CtwIca1J133sM9fc/4cAfLXN3r4TpnS0BDY= +github.com/orange-cloudavenue/netbackup-sdk-go v0.0.2-0.20230928095122-108fd1ec28ae h1:eOxAQKxWiC1iJzhhLW2bD4UCe55viWa0v3IejsbKjdE= +github.com/orange-cloudavenue/netbackup-sdk-go v0.0.2-0.20230928095122-108fd1ec28ae/go.mod h1:KaxPSw6c1JNm+F9YRydHVEhoNjIfrjadKl0BPhRLPvM= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -299,6 +303,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= @@ -329,6 +334,7 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -337,6 +343,8 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -345,6 +353,7 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 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= diff --git a/internal/client/client.go b/internal/client/client.go index 080c4ac3..1bc0be86 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -7,6 +7,8 @@ import ( "fmt" "net/url" + netbackupclient "github.com/orange-cloudavenue/netbackup-sdk-go" + "github.com/vmware/go-vcloud-director/v2/govcd" apiclient "github.com/orange-cloudavenue/cloudavenue-sdk-go" @@ -20,6 +22,8 @@ var ( // ErrConfigureVmware is returned when the configuration of vmware failed. ErrConfigureVmware = errors.New("error configuring vmware") ErrVCDVersionEmpty = errors.New("empty vcd version") + // ErrConfigureNetBackup is returned when the configuration of netbackup failed. + ErrConfigureNetBackup = errors.New("error configuring netbackup") ) // CloudAvenue is the main struct for the CloudAvenue client. @@ -40,6 +44,12 @@ type CloudAvenue struct { Vmware *govcd.VCDClient urlVmware *url.URL VCDVersion string + + // API NetBackup + NetBackupClient *netbackupclient.Client + NetBackupURL string + NetBackupUser string + NetBackupPassword string } // New creates a new CloudAvenue client. @@ -76,6 +86,19 @@ func (c *CloudAvenue) New() (*CloudAvenue, error) { return nil, fmt.Errorf("%w : %w", ErrConfigureVmware, err) } + // API NetBackup + if c.NetBackupURL != "" && c.NetBackupUser != "" && c.NetBackupPassword != "" { + c.NetBackupClient, err = netbackupclient.New(netbackupclient.Opts{ + APIEndpoint: c.NetBackupURL, + Username: c.NetBackupUser, + Password: c.NetBackupPassword, + Debug: false, + }) + if err != nil { + return nil, fmt.Errorf("%w : %w", ErrConfigureNetBackup, err) + } + } + return c, nil } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index f1a16c4f..0a8d3e87 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -47,7 +47,7 @@ func (p *cloudavenueProvider) Schema(ctx context.Context, _ provider.SchemaReque resp.Schema = providerSchema(ctx) } -func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { +func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { //nolint:gocyclo var config cloudavenueProviderModel resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) @@ -62,6 +62,9 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config password := os.Getenv("CLOUDAVENUE_PASSWORD") org := os.Getenv("CLOUDAVENUE_ORG") vdc := os.Getenv("CLOUDAVENUE_VDC") + netbackupURL := os.Getenv("NETBACKUP_URL") + netbackupUser := os.Getenv("NETBACKUP_USER") + netbackupPassword := os.Getenv("NETBACKUP_PASSWORD") if !config.URL.IsNull() && config.URL.ValueString() != "" { urlCloudAvenue = config.URL.ValueString() @@ -81,6 +84,15 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config if !config.VDC.IsNull() && config.VDC.ValueString() != "" { vdc = config.VDC.ValueString() } + if !config.NetBackupURL.IsNull() { + netbackupURL = config.NetBackupURL.ValueString() + } + if !config.NetBackupUser.IsNull() { + netbackupUser = config.NetBackupUser.ValueString() + } + if !config.NetBackupPassword.IsNull() { + netbackupPassword = config.NetBackupPassword.ValueString() + } // Default URL to the public Cloud Avenue API if not set. if urlCloudAvenue == "" { @@ -102,7 +114,7 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config path.Root("password"), "Missing Cloud Avenue API Password", "The provider cannot create the Cloud Avenue API client as there is a missing or empty value for the Cloud Avenue API password. "+ - "Set the host value in the configuration or use the CLOUDAVENUE_PASWWORD environment variable. "+ + "Set the host value in the configuration or use the CLOUDAVENUE_PASSWORD environment variable. "+ "If either is already set, ensure the value is not empty.", ) } @@ -115,6 +127,28 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config "If either is already set, ensure the value is not empty.", ) } + // Default URL to the public NetBackup API if not set. + if netbackupURL == "" { + netbackupURL = "https://backup1.cloudavenue.orange-business.com/NetBackupSelfServiceNetBackupPanels/Api" + } + if netbackupUser == "" && netbackupPassword != "" { + 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 netbackupPassword == "" && netbackupUser != "" { + 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 @@ -125,6 +159,10 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config ctx = tflog.SetField(ctx, "cloudavenue_org", org) ctx = tflog.SetField(ctx, "cloudavenue_password", password) ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "cloudavenue_password") + ctx = tflog.SetField(ctx, "netbackup_host", netbackupURL) + ctx = tflog.SetField(ctx, "netbackup_username", netbackupUser) + ctx = tflog.SetField(ctx, "netbackup_password", netbackupPassword) + ctx = tflog.MaskFieldValuesWithFieldKeys(ctx, "netbackup_password") tflog.Debug(ctx, "Creating CloudAvenue client") @@ -137,6 +175,9 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config TerraformVersion: req.TerraformVersion, CloudAvenueVersion: p.version, VCDVersion: VCDVersion, + NetBackupURL: netbackupURL, + NetBackupUser: netbackupUser, + NetBackupPassword: netbackupPassword, } cA, err := cloudAvenue.New() @@ -174,6 +215,14 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config "VMWare VCD version is empty", ) return + case errors.Is(err, client.ErrConfigureNetBackup): + resp.Diagnostics.AddError( + "Unable to Configure NetBackup Client", + "An unexpected error occurred when creating the NetBackup Client. "+ + "If the error is not clear, please contact the provider developers.\n\n"+ + "NetBackup Client Error: "+err.Error(), + ) + return default: resp.Diagnostics.AddError( "Unable to Create Cloud Avenue API Client", diff --git a/internal/provider/provider_schema.go b/internal/provider/provider_schema.go index 09b62783..b96a6eaa 100644 --- a/internal/provider/provider_schema.go +++ b/internal/provider/provider_schema.go @@ -41,6 +41,25 @@ func providerSchema(_ context.Context) schema.Schema { MarkdownDescription: "The VDC used on Cloud Avenue API. Can also be set with the `CLOUDAVENUE_VDC` environment variable.", Optional: true, }, + "netbackup_url": schema.StringAttribute{ + MarkdownDescription: "The URL of the NetBackup API. Can also be set with the `NETBACKUP_URL` environment variable.", + Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^https?:\/\/\S+\w$`), + "must end with a letter", + ), + }, + }, + "netbackup_user": schema.StringAttribute{ + MarkdownDescription: "The username to use to connect to the NetBackup API. Can also be set with the `NETBACKUP_USER` environment variable.", + Optional: true, + }, + "netbackup_password": schema.StringAttribute{ + MarkdownDescription: "The password to use to connect to the NetBackup API. Can also be set with the `NETBACKUP_PASSWORD` environment variable.", + Sensitive: true, + Optional: true, + }, }, } } diff --git a/internal/provider/provider_types.go b/internal/provider/provider_types.go index 8fb89d5b..7ff47e5a 100644 --- a/internal/provider/provider_types.go +++ b/internal/provider/provider_types.go @@ -3,9 +3,12 @@ package provider import "github.com/hashicorp/terraform-plugin-framework/types" type cloudavenueProviderModel struct { - URL types.String `tfsdk:"url"` - User types.String `tfsdk:"user"` - Password types.String `tfsdk:"password"` - Org types.String `tfsdk:"org"` - VDC types.String `tfsdk:"vdc"` + URL types.String `tfsdk:"url"` + User types.String `tfsdk:"user"` + Password types.String `tfsdk:"password"` + Org types.String `tfsdk:"org"` + VDC types.String `tfsdk:"vdc"` + NetBackupURL types.String `tfsdk:"netbackup_url"` + NetBackupUser types.String `tfsdk:"netbackup_user"` + NetBackupPassword types.String `tfsdk:"netbackup_password"` }