From 4d778ab39cac029165b3959c4d22cb820650777d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Tue, 3 Oct 2023 15:57:46 -0700 Subject: [PATCH 01/26] Add mail server config resource --- pkg/artifactory/provider/framework.go | 1 + .../resource_artifactory_mail_server.go | 332 ++++++++++++++++++ .../resource_artifactory_mail_server_test.go | 1 + 3 files changed, 334 insertions(+) create mode 100644 pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go create mode 100644 pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go diff --git a/pkg/artifactory/provider/framework.go b/pkg/artifactory/provider/framework.go index d1a6821d..742c3a52 100644 --- a/pkg/artifactory/provider/framework.go +++ b/pkg/artifactory/provider/framework.go @@ -176,6 +176,7 @@ func (p *ArtifactoryProvider) Resources(ctx context.Context) []func() resource.R configuration.NewLdapSettingResource, configuration.NewLdapGroupSettingResource, configuration.NewBackupResource, + configuration.NewMailServerResource, } } diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go new file mode 100644 index 00000000..2ba521c8 --- /dev/null +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go @@ -0,0 +1,332 @@ +package configuration + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + utilfw "github.com/jfrog/terraform-provider-shared/util/fw" + utilsdk "github.com/jfrog/terraform-provider-shared/util/sdk" + "gopkg.in/yaml.v3" +) + +type MailServerAPIModel struct { + Enabled bool `xml:"enabled" yaml:"enabled"` + ArtifactoryURL string `xml:"artifactoryUrl" yaml:"artifactoryUrl"` + From string `xml:"from" yaml:"from"` + Host string `xml:"host" yaml:"host"` + Username string `xml:"username" yaml:"username"` + Password string `xml:"password" yaml:"password"` + Port int64 `xml:"port" yaml:"port"` + SubjectPrefix string `xml:"subjectPrefix>repositoryRef" yaml:"subjectPrefix"` + UseSSL bool `xml:"ssl" yaml:"ssl"` + UseTLS bool `xml:"tls" yaml:"tls"` +} + +type MailServer struct { + Server *MailServerAPIModel `xml:"mailServer"` +} + +type MailServerResourceModel struct { + Enabled types.Bool `tfsdk:"enabled"` + ArtifactoryURL types.String `tfsdk:"artifactory_url"` + From types.String `tfsdk:"from"` + Host types.String `tfsdk:"host"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + Port types.Int64 `tfsdk:"port"` + SubjectPrefix types.String `tfsdk:"subject_prefix"` + UseSSL types.Bool `tfsdk:"use_ssl"` + UseTLS types.Bool `tfsdk:"use_tls"` +} + +func (r *MailServerResourceModel) ToAPIModel(ctx context.Context, mailServer *MailServerAPIModel) diag.Diagnostics { + // Convert from Terraform resource model into API model + *mailServer = MailServerAPIModel{ + Enabled: r.Enabled.ValueBool(), + ArtifactoryURL: r.ArtifactoryURL.ValueString(), + From: r.From.ValueString(), + Host: r.Host.ValueString(), + Username: r.Username.ValueString(), + Password: r.Password.ValueString(), + Port: r.Port.ValueInt64(), + SubjectPrefix: r.SubjectPrefix.ValueString(), + UseSSL: r.UseSSL.ValueBool(), + UseTLS: r.UseTLS.ValueBool(), + } + + return nil +} + +func (r *MailServerResourceModel) FromAPIModel(ctx context.Context, mailServer *MailServerAPIModel) diag.Diagnostics { + r.Enabled = types.BoolValue(mailServer.Enabled) + r.ArtifactoryURL = types.StringValue(mailServer.ArtifactoryURL) + r.From = types.StringValue(mailServer.From) + r.Host = types.StringValue(mailServer.Host) + r.Username = types.StringValue(mailServer.Username) + r.Password = types.StringValue(mailServer.Password) + r.Port = types.Int64Value(mailServer.Port) + r.SubjectPrefix = types.StringValue(mailServer.SubjectPrefix) + r.UseSSL = types.BoolValue(mailServer.UseSSL) + r.UseTLS = types.BoolValue(mailServer.UseTLS) + + return nil +} + +func NewMailServerResource() resource.Resource { + return &MailServerResource{} +} + +type MailServerResource struct { + ProviderData utilsdk.ProvderMetadata +} + +func (r *MailServerResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "artifactory_mail_server" +} + +func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Provides an Artifactory backup config resource. This resource configuration corresponds to backup config block in system configuration XML (REST endpoint: artifactory/api/system/configuration). Manages the automatic and periodic backups of the entire Artifactory instance.", + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + MarkdownDescription: "When set, mail notifications are enabled.", + Required: true, + }, + "artifactory_url": schema.StringAttribute{ + MarkdownDescription: "The Artifactory URL to to link to in all outgoing messages.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "from": schema.StringAttribute{ + MarkdownDescription: "The 'from' address header to use in all outgoing messages.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "host": schema.StringAttribute{ + MarkdownDescription: "The mail server IP address / DNS.", + Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "username": schema.StringAttribute{ + MarkdownDescription: "The username for authentication with the mail server.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "password": schema.StringAttribute{ + MarkdownDescription: "The password for authentication with the mail server.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "port": schema.Int64Attribute{ + MarkdownDescription: "The port number of the mail server.", + Required: true, + }, + "subject_prefix": schema.StringAttribute{ + MarkdownDescription: "A prefix to use for the subject of all outgoing mails.", + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtLeast(1), + }, + }, + "use_ssl": schema.BoolAttribute{ + MarkdownDescription: "When set to 'true', uses a secure connection to the mail server.", + Optional: true, + Default: booldefault.StaticBool(false), + }, + "use_tls": schema.BoolAttribute{ + MarkdownDescription: "When set to 'true', uses Transport Layer Security when connecting to the mail server.", + Optional: true, + Default: booldefault.StaticBool(false), + }, + }, + } +} + +func (r *MailServerResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + r.ProviderData = req.ProviderData.(utilsdk.ProvderMetadata) +} + +func (r *MailServerResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan *MailServerResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + var mailServer MailServerAPIModel + resp.Diagnostics.Append(plan.ToAPIModel(ctx, &mailServer)...) + if resp.Diagnostics.HasError() { + return + } + + /* EXPLANATION FOR BELOW CONSTRUCTION USAGE. + + There is a difference in xml structure usage between GET and PATCH calls of API: /artifactory/api/system/configuration. + + GET call structure has "backups -> backup -> Array of backup config blocks". + + PATCH call structure has "backups -> Name/Key of backup that is being patched -> config block of the backup being patched". + + Since the Name/Key is dynamic string, following nested map of string structs are constructed to match the usage of PATCH call. + + See https://www.jfrog.com/confluence/display/JFROG/Artifactory+YAML+Configuration for patching system configuration + using YAML + */ + var constructBody = map[string]MailServerAPIModel{} + constructBody["mailServer"] = mailServer + content, err := yaml.Marshal(&constructBody) + if err != nil { + utilfw.UnableToCreateResourceError(resp, err.Error()) + return + } + + err = SendConfigurationPatch(content, r.ProviderData) + if err != nil { + utilfw.UnableToCreateResourceError(resp, err.Error()) + return + } + + // Assign the resource ID for the resource in the state + plan.Host = types.StringValue(mailServer.Host) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MailServerResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state *MailServerResourceModel + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + var mailServer MailServer + _, err := r.ProviderData.Client.R(). + SetResult(&mailServer). + Get("artifactory/api/system/configuration") + if err != nil { + utilfw.UnableToRefreshResourceError(resp, "failed to retrieve data from API: /artifactory/api/system/configuration during Read") + return + } + + if mailServer.Server == nil { + resp.Diagnostics.AddAttributeWarning( + path.Root("key"), + "no mail server found", + "", + ) + resp.State.RemoveResource(ctx) + return + } + + // Convert from the API data model to the Terraform data model + // and refresh any attribute values. + resp.Diagnostics.Append(state.FromAPIModel(ctx, mailServer.Server)...) + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *MailServerResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan *MailServerResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + + // Convert from Terraform data model into API data model + var mailServer MailServerAPIModel + resp.Diagnostics.Append(plan.ToAPIModel(ctx, &mailServer)...) + + /* EXPLANATION FOR BELOW CONSTRUCTION USAGE. + + There is a difference in xml structure usage between GET and PATCH calls of API: /artifactory/api/system/configuration. + + GET call structure has "backups -> backup -> Array of backup config blocks". + + PATCH call structure has "backups -> Name/Key of backup that is being patched -> config block of the backup being patched". + + Since the Name/Key is dynamic string, following nested map of string structs are constructed to match the usage of PATCH call. + + See https://www.jfrog.com/confluence/display/JFROG/Artifactory+YAML+Configuration for patching system configuration + using YAML + */ + var constructBody = map[string]MailServerAPIModel{} + constructBody["mailServer"] = mailServer + content, err := yaml.Marshal(&constructBody) + if err != nil { + utilfw.UnableToUpdateResourceError(resp, err.Error()) + return + } + + err = SendConfigurationPatch(content, r.ProviderData) + if err != nil { + utilfw.UnableToUpdateResourceError(resp, err.Error()) + return + } + + resp.Diagnostics.Append(plan.FromAPIModel(ctx, &mailServer)...) + if resp.Diagnostics.HasError() { + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *MailServerResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state MailServerResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + deleteMailServerConfig := `mailServer: ~` + + err := SendConfigurationPatch([]byte(deleteMailServerConfig), r.ProviderData) + if err != nil { + utilfw.UnableToDeleteResourceError(resp, err.Error()) + return + } + + // If the logic reaches here, it implicitly succeeded and will remove + // the resource from state if there are no other errors. +} + +// ImportState imports the resource into the Terraform state. +func (r *MailServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("host"), req, resp) +} diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go new file mode 100644 index 00000000..212aef10 --- /dev/null +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go @@ -0,0 +1 @@ +package configuration_test From 93c1e484427616df3758bb20ffc9fd743af1cf53 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 4 Oct 2023 13:18:37 -0700 Subject: [PATCH 02/26] Add 'artifactory_mail_server' resource --- docs/resources/mail_server.md | 57 ++++++ .../artifactory_mail_server/import.sh | 1 + .../artifactory_mail_server/resource.tf | 12 ++ .../resource_artifactory_backup.go | 4 +- .../resource_artifactory_backup_test.go | 2 +- .../resource_artifactory_mail_server.go | 14 +- .../resource_artifactory_mail_server_test.go | 192 ++++++++++++++++++ templates/resources/mail_server.md.tmpl | 29 +++ 8 files changed, 304 insertions(+), 7 deletions(-) create mode 100644 docs/resources/mail_server.md create mode 100644 examples/resources/artifactory_mail_server/import.sh create mode 100644 examples/resources/artifactory_mail_server/resource.tf create mode 100644 templates/resources/mail_server.md.tmpl diff --git a/docs/resources/mail_server.md b/docs/resources/mail_server.md new file mode 100644 index 00000000..1d1b0ee0 --- /dev/null +++ b/docs/resources/mail_server.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "artifactory_mail_server Resource - terraform-provider-artifactory" +subcategory: "Configuration" +--- + +# Artifactory Mail Server Resource + +Provides an Artifactory Mail Server resource. This can be used to create and manage Artifactory mail server configuration. + +## Example Usages + +```terraform +resource "artifactory_mail_server" "mymailserver" { + enabled = true + artifactory_url = "http://tempurl.org" + from = "test@jfrog.com" + host = "http://tempurl.org" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + use_ssl = true + use_tls = true +} +``` + +## Argument reference + + +## Schema + +### Required + +- `enabled` (Boolean) When set, mail notifications are enabled. +- `host` (String) The mail server IP address / DNS. +- `port` (Number) The port number of the mail server. + +### Optional + +- `artifactory_url` (String) The Artifactory URL to to link to in all outgoing messages. +- `from` (String) The 'from' address header to use in all outgoing messages. +- `password` (String) The password for authentication with the mail server. +- `subject_prefix` (String) A prefix to use for the subject of all outgoing mails. +- `use_ssl` (Boolean) When set to 'true', uses a secure connection to the mail server. +- `use_tls` (Boolean) When set to 'true', uses Transport Layer Security when connecting to the mail server. +- `username` (String) The username for authentication with the mail server. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import artifactory_mail_server.my-mail-server mymailserver +``` + +~>The `password` attribute is not retrievable from Artifactory thus there will be state drift after importing this resource. diff --git a/examples/resources/artifactory_mail_server/import.sh b/examples/resources/artifactory_mail_server/import.sh new file mode 100644 index 00000000..46fe0436 --- /dev/null +++ b/examples/resources/artifactory_mail_server/import.sh @@ -0,0 +1 @@ +terraform import artifactory_mail_server.my-mail-server mymailserver \ No newline at end of file diff --git a/examples/resources/artifactory_mail_server/resource.tf b/examples/resources/artifactory_mail_server/resource.tf new file mode 100644 index 00000000..4389f6ad --- /dev/null +++ b/examples/resources/artifactory_mail_server/resource.tf @@ -0,0 +1,12 @@ +resource "artifactory_mail_server" "mymailserver" { + enabled = true + artifactory_url = "http://tempurl.org" + from = "test@jfrog.com" + host = "http://tempurl.org" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + use_ssl = true + use_tls = true +} \ No newline at end of file diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_backup.go b/pkg/artifactory/resource/configuration/resource_artifactory_backup.go index 1f957445..e76e3e2b 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_backup.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_backup.go @@ -18,7 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" utilfw "github.com/jfrog/terraform-provider-shared/util/fw" utilsdk "github.com/jfrog/terraform-provider-shared/util/sdk" - validatorfw "github.com/jfrog/terraform-provider-shared/validator/fw" + validatorfw_string "github.com/jfrog/terraform-provider-shared/validator/fw/string" "gopkg.in/yaml.v3" ) @@ -136,7 +136,7 @@ func (r *BackupResource) Schema(ctx context.Context, req resource.SchemaRequest, MarkdownDescription: "Cron expression to control the backup frequency.", Required: true, Validators: []validator.String{ - validatorfw.IsCron(), + validatorfw_string.IsCron(), }, }, "retention_period_hours": schema.Int64Attribute{ diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go b/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go index 36f4bafa..45e2271b 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go @@ -139,7 +139,7 @@ func TestAccBackup_invalid_cron(t *testing.T) { { Config: config, ResourceName: "artifactory_backup.invalid-cron-test", - ExpectError: regexp.MustCompile("value must match be a valid cron expression"), + ExpectError: regexp.MustCompile("value must be a valid cron expression"), }, }, }) diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go index 2ba521c8..01f4beef 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" utilfw "github.com/jfrog/terraform-provider-shared/util/fw" utilsdk "github.com/jfrog/terraform-provider-shared/util/sdk" + validatorfw_string "github.com/jfrog/terraform-provider-shared/validator/fw/string" "gopkg.in/yaml.v3" ) @@ -26,7 +27,7 @@ type MailServerAPIModel struct { Username string `xml:"username" yaml:"username"` Password string `xml:"password" yaml:"password"` Port int64 `xml:"port" yaml:"port"` - SubjectPrefix string `xml:"subjectPrefix>repositoryRef" yaml:"subjectPrefix"` + SubjectPrefix string `xml:"subjectPrefix" yaml:"subjectPrefix"` UseSSL bool `xml:"ssl" yaml:"ssl"` UseTLS bool `xml:"tls" yaml:"tls"` } @@ -72,7 +73,6 @@ func (r *MailServerResourceModel) FromAPIModel(ctx context.Context, mailServer * r.From = types.StringValue(mailServer.From) r.Host = types.StringValue(mailServer.Host) r.Username = types.StringValue(mailServer.Username) - r.Password = types.StringValue(mailServer.Password) r.Port = types.Int64Value(mailServer.Port) r.SubjectPrefix = types.StringValue(mailServer.SubjectPrefix) r.UseSSL = types.BoolValue(mailServer.UseSSL) @@ -95,7 +95,7 @@ func (r *MailServerResource) Metadata(ctx context.Context, req resource.Metadata func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - MarkdownDescription: "Provides an Artifactory backup config resource. This resource configuration corresponds to backup config block in system configuration XML (REST endpoint: artifactory/api/system/configuration). Manages the automatic and periodic backups of the entire Artifactory instance.", + MarkdownDescription: "Provides an Artifactory mail server config resource. This resource configuration corresponds to mail server config block in system configuration XML (REST endpoint: artifactory/api/system/configuration). Manages mail server settings of the Artifactory instance.", Attributes: map[string]schema.Attribute{ "enabled": schema.BoolAttribute{ MarkdownDescription: "When set, mail notifications are enabled.", @@ -106,6 +106,7 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ Optional: true, Validators: []validator.String{ stringvalidator.LengthAtLeast(1), + validatorfw_string.IsURLHttpOrHttps(), }, }, "from": schema.StringAttribute{ @@ -113,6 +114,7 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ Optional: true, Validators: []validator.String{ stringvalidator.LengthAtLeast(1), + validatorfw_string.IsEmail(), }, }, "host": schema.StringAttribute{ @@ -153,11 +155,13 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ "use_ssl": schema.BoolAttribute{ MarkdownDescription: "When set to 'true', uses a secure connection to the mail server.", Optional: true, + Computed: true, Default: booldefault.StaticBool(false), }, "use_tls": schema.BoolAttribute{ MarkdownDescription: "When set to 'true', uses Transport Layer Security when connecting to the mail server.", Optional: true, + Computed: true, Default: booldefault.StaticBool(false), }, }, @@ -240,7 +244,7 @@ func (r *MailServerResource) Read(ctx context.Context, req resource.ReadRequest, if mailServer.Server == nil { resp.Diagnostics.AddAttributeWarning( - path.Root("key"), + path.Root("host"), "no mail server found", "", ) @@ -328,5 +332,7 @@ func (r *MailServerResource) Delete(ctx context.Context, req resource.DeleteRequ // ImportState imports the resource into the Terraform state. func (r *MailServerResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // "host" attribute is used here but it's a noop. There's only ever one mail server on Artifactory + // so there's no need to use ID to fetch. resource.ImportStatePassthroughID(ctx, path.Root("host"), req, resp) } diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go index 212aef10..39cbd6f7 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go @@ -1 +1,193 @@ package configuration_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/jfrog/terraform-provider-artifactory/v9/pkg/acctest" + "github.com/jfrog/terraform-provider-artifactory/v9/pkg/artifactory/resource/configuration" + "github.com/jfrog/terraform-provider-shared/testutil" + utilsdk "github.com/jfrog/terraform-provider-shared/util/sdk" +) + +func TestAccMailServer_full(t *testing.T) { + _, fqrn, resourceName := testutil.MkNames("mailserver-", "artifactory_mail_server") + + const mailServerTemplate = ` + resource "artifactory_mail_server" "{{ .resourceName }}" { + enabled = true + artifactory_url = "{{ .artifactory_url }}" + from = "{{ .from }}" + host = "{{ .host }}" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + }` + + testData := map[string]string{ + "resourceName": resourceName, + "artifactory_url": "http://tempurl.org", + "from": "test@jfrog.com", + "host": "http://tempurl.org", + } + + const mailServerTemplateUpdate = ` + resource "artifactory_mail_server" "{{ .resourceName }}" { + enabled = true + artifactory_url = "{{ .artifactory_url }}" + from = "{{ .from }}" + host = "{{ .host }}" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + use_ssl = true + use_tls = true + }` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + CheckDestroy: testAccMailServerDestroy(resourceName), + + Steps: []resource.TestStep{ + { + Config: utilsdk.ExecuteTemplate(fqrn, mailServerTemplate, testData), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "enabled", "true"), + resource.TestCheckResourceAttr(fqrn, "artifactory_url", testData["artifactory_url"]), + resource.TestCheckResourceAttr(fqrn, "from", testData["from"]), + resource.TestCheckResourceAttr(fqrn, "host", testData["host"]), + resource.TestCheckResourceAttr(fqrn, "username", "test-user"), + resource.TestCheckResourceAttr(fqrn, "password", "test-password"), + resource.TestCheckResourceAttr(fqrn, "port", "25"), + resource.TestCheckResourceAttr(fqrn, "subject_prefix", "[Test]"), + resource.TestCheckResourceAttr(fqrn, "use_ssl", "false"), + resource.TestCheckResourceAttr(fqrn, "use_tls", "false"), + ), + }, + { + Config: utilsdk.ExecuteTemplate(fqrn, mailServerTemplateUpdate, testData), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "enabled", "true"), + resource.TestCheckResourceAttr(fqrn, "artifactory_url", testData["artifactory_url"]), + resource.TestCheckResourceAttr(fqrn, "from", testData["from"]), + resource.TestCheckResourceAttr(fqrn, "host", testData["host"]), + resource.TestCheckResourceAttr(fqrn, "username", "test-user"), + resource.TestCheckResourceAttr(fqrn, "password", "test-password"), + resource.TestCheckResourceAttr(fqrn, "port", "25"), + resource.TestCheckResourceAttr(fqrn, "subject_prefix", "[Test]"), + resource.TestCheckResourceAttr(fqrn, "use_ssl", "true"), + resource.TestCheckResourceAttr(fqrn, "use_tls", "true"), + ), + }, + { + ResourceName: fqrn, + ImportStateId: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIdentifierAttribute: "host", + ImportStateVerifyIgnore: []string{"password"}, + }, + }, + }) +} + +func TestAccMailServer_invalid_from(t *testing.T) { + _, fqrn, resourceName := testutil.MkNames("mailserver-", "artifactory_mail_server") + + template := ` + resource "artifactory_mail_server" "{{ .resourceName }}" { + enabled = true + artifactory_url = "http://tempurl.org" + from = "invalid-email" + host = "http://tempurl.org" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + use_ssl = true + use_tls = true + }` + + testData := map[string]string{ + "resourceName": resourceName, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + Steps: []resource.TestStep{ + { + Config: utilsdk.ExecuteTemplate(fqrn, template, testData), + ResourceName: resourceName, + ExpectError: regexp.MustCompile("value must be a valid email address"), + }, + }, + }) +} + +func TestAccMailServer_invalid_artifactory_url(t *testing.T) { + _, fqrn, resourceName := testutil.MkNames("mailserver-", "artifactory_mail_server") + + template := ` + resource "artifactory_mail_server" "{{ .resourceName }}" { + enabled = true + artifactory_url = "invalid-url" + from = "test-user@jfrog.com" + host = "http://tempurl.org" + username = "test-user" + password = "test-password" + port = 25 + subject_prefix = "[Test]" + use_ssl = true + use_tls = true + }` + + testData := map[string]string{ + "resourceName": resourceName, + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + Steps: []resource.TestStep{ + { + Config: utilsdk.ExecuteTemplate(fqrn, template, testData), + ResourceName: resourceName, + ExpectError: regexp.MustCompile("value must be a valid URL with host.*"), + }, + }, + }) +} + +func testAccMailServerDestroy(id string) func(*terraform.State) error { + return func(s *terraform.State) error { + client := acctest.Provider.Meta().(utilsdk.ProvderMetadata).Client + + _, ok := s.RootModule().Resources["artifactory_mail_server."+id] + if !ok { + return fmt.Errorf("error: resource id [%s] not found", id) + } + + var mailServer configuration.MailServer + + response, err := client.R().SetResult(&mailServer).Get("artifactory/api/system/configuration") + if err != nil { + return err + } + if response.IsError() { + return fmt.Errorf("got error response for API: /artifactory/api/system/configuration request during Read. Response:%#v", response) + } + + if mailServer.Server != nil { + return fmt.Errorf("error: MailServer config still exists.") + } + + return nil + } +} diff --git a/templates/resources/mail_server.md.tmpl b/templates/resources/mail_server.md.tmpl new file mode 100644 index 00000000..3b2e328c --- /dev/null +++ b/templates/resources/mail_server.md.tmpl @@ -0,0 +1,29 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "{{ .Name }} Resource - {{ .ProviderName }}" +subcategory: "Configuration" +--- + +# Artifactory Mail Server Resource + +Provides an Artifactory Mail Server resource. This can be used to create and manage Artifactory mail server configuration. + +## Example Usages + +{{tffile (printf "examples/resources/%s/resource.tf" .Name) }} + +## Argument reference + +{{ .SchemaMarkdown | trimspace }} + +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" .ImportFile }} + +~>The `password` attribute is not retrievable from Artifactory thus there will be state drift after importing this resource. + +{{- end }} From 01f723a8376f1facfbabf904767b670c762ce04d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 4 Oct 2023 15:23:11 -0700 Subject: [PATCH 03/26] Provider code tidy up --- pkg/artifactory/provider/framework.go | 3 +-- pkg/artifactory/provider/provider.go | 4 ++++ pkg/artifactory/provider/sdkv2.go | 3 --- 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 pkg/artifactory/provider/provider.go diff --git a/pkg/artifactory/provider/framework.go b/pkg/artifactory/provider/framework.go index 742c3a52..f32aea08 100644 --- a/pkg/artifactory/provider/framework.go +++ b/pkg/artifactory/provider/framework.go @@ -127,7 +127,7 @@ func (p *ArtifactoryProvider) Configure(ctx context.Context, req provider.Config fmt.Sprintf("%v", err), ) } - if config.CheckLicense.IsNull() || config.CheckLicense.ValueBool() == true { + if config.CheckLicense.IsNull() || config.CheckLicense.ValueBool() { licenseErr := utilsdk.CheckArtifactoryLicense(restyBase, "Enterprise", "Commercial", "Edge") if licenseErr != nil { resp.Diagnostics.AddError( @@ -159,7 +159,6 @@ func (p *ArtifactoryProvider) Configure(ctx context.Context, req provider.Config Client: restyBase, ArtifactoryVersion: version, } - } // Resources satisfies the provider.Provider interface for ArtifactoryProvider. diff --git a/pkg/artifactory/provider/provider.go b/pkg/artifactory/provider/provider.go new file mode 100644 index 00000000..b6841db5 --- /dev/null +++ b/pkg/artifactory/provider/provider.go @@ -0,0 +1,4 @@ +package provider + +var Version = "9.0.0" // needs to be exported so make file can update this +var productId = "terraform-provider-artifactory/" + Version diff --git a/pkg/artifactory/provider/sdkv2.go b/pkg/artifactory/provider/sdkv2.go index 5428e958..a651c202 100644 --- a/pkg/artifactory/provider/sdkv2.go +++ b/pkg/artifactory/provider/sdkv2.go @@ -13,9 +13,6 @@ import ( "github.com/jfrog/terraform-provider-shared/validator" ) -var Version = "7.0.0" // needs to be exported so make file can update this -var productId = "terraform-provider-artifactory/" + Version - // SdkV2 Artifactory provider that supports configuration via Access Token // Supported resources are repos, users, groups, replications, and permissions func SdkV2() *schema.Provider { From ff5185a84d417c5efecf256528e647fc83466eb2 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 4 Oct 2023 15:53:30 -0700 Subject: [PATCH 04/26] Update shared lib version --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 88e07c70..b49b3259 100644 --- a/go.mod +++ b/go.mod @@ -10,13 +10,13 @@ require ( github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/terraform-plugin-docs v0.16.0 github.com/hashicorp/terraform-plugin-framework v1.3.5 - github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 + github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.18.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.28.0 github.com/hashicorp/terraform-plugin-testing v1.5.1 - github.com/jfrog/terraform-provider-shared v1.19.0 + github.com/jfrog/terraform-provider-shared v1.20.0 github.com/sethvargo/go-password v0.2.0 github.com/stretchr/testify v1.7.2 golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 diff --git a/go.sum b/go.sum index 5c825af4..dbaa09d9 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFcc github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA= github.com/hashicorp/terraform-plugin-framework v1.3.5 h1:FJ6s3CVWVAxlhiF/jhy6hzs4AnPHiflsp9KgzTGl1wo= github.com/hashicorp/terraform-plugin-framework v1.3.5/go.mod h1:2gGDpWiTI0irr9NSTLFAKlTi6KwGti3AoU19rFqU30o= -github.com/hashicorp/terraform-plugin-framework-validators v0.10.0 h1:4L0tmy/8esP6OcvocVymw52lY0HyQ5OxB7VNl7k4bS0= -github.com/hashicorp/terraform-plugin-framework-validators v0.10.0/go.mod h1:qdQJCdimB9JeX2YwOpItEu+IrfoJjWQ5PhLpAOMDQAE= +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.18.0 h1:IwTkOS9cOW1ehLd/rG0y+u/TGLK9y6fGoBjXVUquzpE= github.com/hashicorp/terraform-plugin-go v0.18.0/go.mod h1:l7VK+2u5Kf2y+A+742GX0ouLut3gttudmvMgN0PA74Y= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= @@ -116,8 +116,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= -github.com/jfrog/terraform-provider-shared v1.19.0 h1:4/CgvSTqhf00eHMo8q+xL2/N8Lng7T+Pw5GbNSUwxRs= -github.com/jfrog/terraform-provider-shared v1.19.0/go.mod h1:JvTKRAXMQyX6gQjESY+YK2lJLeW8uKTVHar5HDTnvp0= +github.com/jfrog/terraform-provider-shared v1.20.0 h1:T5AFbn4Su3tlNZTIXwb8Bi4vq/LZMFH312V2z8d3IsI= +github.com/jfrog/terraform-provider-shared v1.20.0/go.mod h1:KEYnVOggRuQT6qLR05ra0QfQa0SeYnkMnN0ZqIgQHqI= github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= From 4b815a4b6444a6db4e17316304745e22b30fcf7a Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Wed, 4 Oct 2023 16:25:03 -0700 Subject: [PATCH 05/26] Update CHANGELOG --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81a26b86..8f5b7251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 9.4.0 (Oct 5, 2023) + +FEATURES: + +* resource/artifactory_mail_server: add a new resource for managing mail server configuration. PR: [#819](https://github.com/jfrog/terraform-provider-artifactory/pull/819) Issue: [#735](https://github.com/jfrog/terraform-provider-artifactory/issues/735) + ## 9.3.0 (Oct 3, 2023). Tested on Artifactory 7.68.13 with Terraform CLI v1.5.7 IMPROVEMENTS: @@ -5,7 +11,6 @@ IMPROVEMENTS: * resource/artifactory_distribution_public_key is migrated to Plugin Framework. PR: [#817](https://github.com/jfrog/terraform-provider-artifactory/pull/817) * resource/artifactory_remote_\*\_repository: Fix incorrect default value for `store_artifacts_locally` attribute in documentation. PR: [#816](https://github.com/jfrog/terraform-provider-artifactory/pull/816) - ## 9.2.1 (Sep 29, 2023). Tested on Artifactory 7.68.11 with Terraform CLI v1.5.7 IMPROVEMENTS: From 839a59a952b687f077a0d4663ddf07e528a293d7 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 12:57:27 -0700 Subject: [PATCH 06/26] Add validator for 'port' attribute --- .../configuration/resource_artifactory_mail_server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go index 01f4beef..92f2f87f 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server.go @@ -3,6 +3,7 @@ package configuration import ( "context" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -105,7 +106,6 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ MarkdownDescription: "The Artifactory URL to to link to in all outgoing messages.", Optional: true, Validators: []validator.String{ - stringvalidator.LengthAtLeast(1), validatorfw_string.IsURLHttpOrHttps(), }, }, @@ -113,7 +113,6 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ MarkdownDescription: "The 'from' address header to use in all outgoing messages.", Optional: true, Validators: []validator.String{ - stringvalidator.LengthAtLeast(1), validatorfw_string.IsEmail(), }, }, @@ -144,6 +143,9 @@ func (r *MailServerResource) Schema(ctx context.Context, req resource.SchemaRequ "port": schema.Int64Attribute{ MarkdownDescription: "The port number of the mail server.", Required: true, + Validators: []validator.Int64{ + int64validator.AtMost(65535), + }, }, "subject_prefix": schema.StringAttribute{ MarkdownDescription: "A prefix to use for the subject of all outgoing mails.", From 4cc7666498a2fd417ed33fd64a3a8c5a645d9d0d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 12:59:57 -0700 Subject: [PATCH 07/26] Bump Pipeline timeout limit --- .jfrog-pipelines/TFproviderTest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 81c37606..179d4f73 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -19,7 +19,7 @@ pipelines: configuration: #nodePool: default priority: 1 - timeoutSeconds: 3600 # 60 minutes + timeoutSeconds: 5400 # 90 minutes runtime: type: image image: From df0ad9afc95b4123a259ea96f1ef27b0c9508e7a Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 13:22:17 -0700 Subject: [PATCH 08/26] Remove default value for `artifactory_scoped_token.expires_in` This fix state drift issue for resource created in v8 and before. Update incorrect URL in documentation --- docs/resources/scoped_token.md | 2 +- .../resource_artifactory_scoped_token.go | 2 - .../resource_artifactory_scoped_token_test.go | 62 ++++++++++++++++++- 3 files changed, 61 insertions(+), 5 deletions(-) diff --git a/docs/resources/scoped_token.md b/docs/resources/scoped_token.md index 0784e653..da3ac117 100644 --- a/docs/resources/scoped_token.md +++ b/docs/resources/scoped_token.md @@ -75,7 +75,7 @@ resource "artifactory_scoped_token" "audience" { - `audiences` (Set of String) A list of the other instances or services that should accept this token identified by their Service-IDs. Limited to total 255 characters. Default to '*@*' if not set. Service ID must begin with valid JFrog service type. Options: jfrt, jfxr, jfpip, jfds, jfmc, jfac, jfevt, jfmd, jfcon, or *. For instructions to retrieve the Artifactory Service ID see this [documentation](https://jfrog.com/help/r/jfrog-rest-apis/get-service-id) - `description` (String) Free text token description. Useful for filtering and managing tokens. Limited to 1024 characters. -- `expires_in` (Number) The amount of time, in seconds, it would take for the token to expire. An admin shall be able to set whether expiry is mandatory, what is the default expiry, and what is the maximum expiry allowed. Must be non-negative. Default value is based on configuration in 'access.config.yaml'. See [API documentation](https://jfrog.com/help/r/jfrog-rest-apis/revoke-token-by-id) for details. Access Token would not be saved by Artifactory if this is less than the persistence threshold value (default to 10800 seconds) set in Access configuration. See [official documentation](https://jfrog.com/help/r/jfrog-platform-administration-documentation/using-the-revocable-and-persistency-thresholds) for details. +- `expires_in` (Number) The amount of time, in seconds, it would take for the token to expire. An admin shall be able to set whether expiry is mandatory, what is the default expiry, and what is the maximum expiry allowed. Must be non-negative. Default value is based on configuration in 'access.config.yaml'. See [API documentation](https://jfrog.com/help/r/jfrog-rest-apis/create-token) for details. Access Token would not be saved by Artifactory if this is less than the persistence threshold value (default to 10800 seconds) set in Access configuration. See [official documentation](https://jfrog.com/help/r/jfrog-platform-administration-documentation/using-the-revocable-and-persistency-thresholds) for details. - `grant_type` (String) The grant type used to authenticate the request. In this case, the only value supported is `client_credentials` which is also the default value if this parameter is not specified. - `include_reference_token` (Boolean) Also create a reference token which can be used like an API key. - `refreshable` (Boolean) Is this token refreshable? Default is `false`. diff --git a/pkg/artifactory/resource/security/resource_artifactory_scoped_token.go b/pkg/artifactory/resource/security/resource_artifactory_scoped_token.go index c41b8bd4..85b159c8 100644 --- a/pkg/artifactory/resource/security/resource_artifactory_scoped_token.go +++ b/pkg/artifactory/resource/security/resource_artifactory_scoped_token.go @@ -16,7 +16,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" @@ -198,7 +197,6 @@ func (r *ScopedTokenResource) Schema(ctx context.Context, req resource.SchemaReq MarkdownDescription: "The amount of time, in seconds, it would take for the token to expire. An admin shall be able to set whether expiry is mandatory, what is the default expiry, and what is the maximum expiry allowed. Must be non-negative. Default value is based on configuration in 'access.config.yaml'. See [API documentation](https://jfrog.com/help/r/jfrog-rest-apis/revoke-token-by-id) for details. Access Token would not be saved by Artifactory if this is less than the persistence threshold value (default to 10800 seconds) set in Access configuration. See [official documentation](https://jfrog.com/help/r/jfrog-platform-administration-documentation/using-the-revocable-and-persistency-thresholds) for details.", Optional: true, Computed: true, - Default: int64default.StaticInt64(0), PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplaceIfConfigured(), int64planmodifier.UseStateForUnknown(), diff --git a/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go b/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go index eb879b5d..63625463 100644 --- a/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go +++ b/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go @@ -91,6 +91,64 @@ func TestAccScopedToken_UpgradeGH_792(t *testing.T) { }) } +func TestAccScopedToken_UpgradeGH_818(t *testing.T) { + _, fqrn, name := testutil.MkNames("test-scope-token", "artifactory_scoped_token") + config := utilsdk.ExecuteTemplate( + "TestAccScopedToken", + `resource "artifactory_user" "test-user" { + name = "testuser" + email = "testuser@tempurl.org" + admin = true + disable_ui_access = false + groups = ["readers"] + password = "Passw0rd!" + } + + resource "artifactory_scoped_token" "{{ .name }}" { + scopes = ["applied-permissions/user"] + username = artifactory_user.test-user.name + }`, + map[string]interface{}{ + "name": name, + }, + ) + + resource.Test(t, resource.TestCase{ + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "artifactory": { + VersionConstraint: "7.2.0", + Source: "registry.terraform.io/jfrog/artifactory", + }, + }, + Config: config, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "username", "testuser"), + resource.TestCheckResourceAttr(fqrn, "scopes.#", "1"), + resource.TestCheckResourceAttr(fqrn, "expires_in", "31536000"), + resource.TestCheckNoResourceAttr(fqrn, "audiences"), + resource.TestCheckResourceAttrSet(fqrn, "access_token"), + resource.TestCheckNoResourceAttr(fqrn, "refresh_token"), + resource.TestCheckNoResourceAttr(fqrn, "reference_token"), + resource.TestCheckResourceAttr(fqrn, "token_type", "Bearer"), + resource.TestCheckResourceAttrSet(fqrn, "subject"), + resource.TestCheckResourceAttrSet(fqrn, "expiry"), + resource.TestCheckResourceAttrSet(fqrn, "issued_at"), + resource.TestCheckResourceAttrSet(fqrn, "issuer"), + ), + ConfigPlanChecks: acctest.ConfigPlanChecks, + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + Config: config, + PlanOnly: true, + ConfigPlanChecks: acctest.ConfigPlanChecks, + }, + }, + }) +} + func scopedTokenUpgradeTestCase(version string, t *testing.T) (*testing.T, resource.TestCase) { _, fqrn, name := testutil.MkNames("test-access-token", "artifactory_scoped_token") @@ -246,7 +304,7 @@ func TestAccScopedToken_WithAttributes(t *testing.T) { scopes = ["applied-permissions/admin", "system:metrics:r"] description = "test description" refreshable = true - expires_in = 31536000 + expires_in = 0 audiences = ["jfrt@1", "jfxr@*"] }`, map[string]interface{}{ @@ -275,7 +333,7 @@ func TestAccScopedToken_WithAttributes(t *testing.T) { resource.TestCheckTypeSetElemAttr(fqrn, "scopes.*", "applied-permissions/admin"), resource.TestCheckTypeSetElemAttr(fqrn, "scopes.*", "system:metrics:r"), resource.TestCheckResourceAttr(fqrn, "refreshable", "true"), - resource.TestCheckResourceAttr(fqrn, "expires_in", "31536000"), + resource.TestCheckResourceAttr(fqrn, "expires_in", "0"), resource.TestCheckResourceAttr(fqrn, "description", "test description"), resource.TestCheckResourceAttr(fqrn, "audiences.#", "2"), resource.TestCheckTypeSetElemAttr(fqrn, "audiences.*", "jfrt@1"), From 82a5e9e032a3522ccf0afc2a905d7db65a83f0d8 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 14:14:29 -0700 Subject: [PATCH 09/26] Revert timeout bump --- .jfrog-pipelines/TFproviderTest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 653dd6a3..d261bf87 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -19,7 +19,7 @@ pipelines: configuration: #nodePool: default priority: 1 - timeoutSeconds: 5400 # 90 minutes + timeoutSeconds: 3600 # 60 minutes runtime: type: image image: From 12cb994517af1b6d2ad7f702a1e5891f52d0be09 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 14:48:22 -0700 Subject: [PATCH 10/26] Update CHANGELOG --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81a26b86..f809ad45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 9.3.1 (Oct 6, 2023) + +BUG FIX: +* resource/artifactory_scoped_token: Remove default value for `expires_in` attribute which should fix state drift when upgrading from 7.11.2 or earlier. Issue: [#818](https://github.com/jfrog/terraform-provider-artifactory/issues/818) PR: [#820](https://github.com/jfrog/terraform-provider-artifactory/pull/820) + ## 9.3.0 (Oct 3, 2023). Tested on Artifactory 7.68.13 with Terraform CLI v1.5.7 IMPROVEMENTS: From 461ee9d81ec384e2d9108b5ff321070032bdcf39 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 15:26:26 -0700 Subject: [PATCH 11/26] Remove unused pipeline code --- .jfrog-pipelines/TFproviderTest.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index d261bf87..21e53a52 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -72,10 +72,6 @@ pipelines: - echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list - sudo apt update - sudo apt install goreleaser - # TODO: enable after migration to repo21 - #- echo "Install SonarQube" - #- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip - #- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ onExecute: - add_run_variables TFProviderRepo=$(echo ${res_GitHubTFProviderRepoJFrog_gitRepoRepositorySshUrl} | sed -e 's/git@/@/g' -e 's/:/\//g') - cd ${res_GitHubTFProviderRepoJFrog_resourcePath} # we need to manually move into the resource path @@ -141,9 +137,6 @@ pipelines: - unset ARTIFACTORY_PASSWORD - export TF_ACC=true - make acceptance - # TODO: enable after migration to repo21 - #- export SONAR_TOKEN=${int_terraform_artifactory_sonarqube_token} - #- make scan - make install onSuccess: - echo "Success" From 4852b18b02531e86a4103f22c71762bc9d4891c9 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 5 Oct 2023 15:43:18 -0700 Subject: [PATCH 12/26] Remove usage of ProtoV5MuxProviderFactories where it isn't needed --- .../resource_artifactory_backup_test.go | 6 +++--- .../resource_artifactory_mail_server_test.go | 6 +++--- ...artifactory_distribution_public_key_test.go | 4 ++-- ...ource_artifactory_permission_target_test.go | 2 +- .../resource_artifactory_scoped_token_test.go | 18 +++++++++--------- ...resource_artifactory_anonymous_user_test.go | 4 ++-- .../resource_artifactory_managed_user_test.go | 2 +- .../user/resource_artifactory_user_test.go | 14 +++++++------- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go b/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go index 45e2271b..5d744a0a 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_backup_test.go @@ -106,7 +106,7 @@ func TestAccBackup_importNotFound(t *testing.T) { ` resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: config, @@ -134,7 +134,7 @@ func TestAccBackup_invalid_cron(t *testing.T) { ` resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: config, @@ -180,7 +180,7 @@ func cronTestCase(cronExpression string, t *testing.T) (*testing.T, resource.Tes return t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckRepo), Steps: []resource.TestStep{ { diff --git a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go index 39cbd6f7..7d792333 100644 --- a/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go +++ b/pkg/artifactory/resource/configuration/resource_artifactory_mail_server_test.go @@ -51,7 +51,7 @@ func TestAccMailServer_full(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccMailServerDestroy(resourceName), Steps: []resource.TestStep{ @@ -120,7 +120,7 @@ func TestAccMailServer_invalid_from(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: utilsdk.ExecuteTemplate(fqrn, template, testData), @@ -154,7 +154,7 @@ func TestAccMailServer_invalid_artifactory_url(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: utilsdk.ExecuteTemplate(fqrn, template, testData), diff --git a/pkg/artifactory/resource/security/resource_artifactory_distribution_public_key_test.go b/pkg/artifactory/resource/security/resource_artifactory_distribution_public_key_test.go index 20690de1..5b2d76f8 100644 --- a/pkg/artifactory/resource/security/resource_artifactory_distribution_public_key_test.go +++ b/pkg/artifactory/resource/security/resource_artifactory_distribution_public_key_test.go @@ -97,7 +97,7 @@ func TestAccDistributionPublicKey_FormatCheck(t *testing.T) { `, resource_name, name, id) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: keyBasic, @@ -112,7 +112,7 @@ func TestAccDistributionPublicKey_Create(t *testing.T) { keyBasic := fmt.Sprintf(template, resource_name, name, name) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDistributionPublicKeyDestroy(fqrn), Steps: []resource.TestStep{ { diff --git a/pkg/artifactory/resource/security/resource_artifactory_permission_target_test.go b/pkg/artifactory/resource/security/resource_artifactory_permission_target_test.go index 82094346..8e79452f 100644 --- a/pkg/artifactory/resource/security/resource_artifactory_permission_target_test.go +++ b/pkg/artifactory/resource/security/resource_artifactory_permission_target_test.go @@ -536,7 +536,7 @@ func TestAccPermissionTarget_MissingRepositories(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testPermissionTargetCheckDestroy(permFqrn), Steps: []resource.TestStep{ { diff --git a/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go b/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go index eb879b5d..08d5cc4a 100644 --- a/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go +++ b/pkg/artifactory/resource/security/resource_artifactory_scoped_token_test.go @@ -188,7 +188,7 @@ func TestAccScopedToken_WithDefaults(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: acctest.VerifyDeleted(fqrn, security.CheckAccessToken), Steps: []resource.TestStep{ { @@ -260,7 +260,7 @@ func TestAccScopedToken_WithAttributes(t *testing.T) { acctest.PreCheck(t) acctest.CreateProject(t, projectKey) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: acctest.VerifyDeleted(fqrn, func(id string, request *resty.Request) (*resty.Response, error) { acctest.DeleteProject(t, projectKey) return security.CheckAccessToken(id, request) @@ -321,7 +321,7 @@ func TestAccScopedToken_WithGroupScope(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: accessTokenConfig, @@ -355,7 +355,7 @@ func TestAccScopedToken_WithInvalidScopes(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: scopedTokenConfig, @@ -463,7 +463,7 @@ func mkAudienceTestCase(prefix string, t *testing.T) (*testing.T, resource.TestC return t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: accessTokenConfig, @@ -496,7 +496,7 @@ func TestAccScopedToken_WithInvalidAudiences(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: scopedTokenConfig, @@ -529,7 +529,7 @@ func TestAccScopedToken_WithTooLongAudiences(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: scopedTokenConfig, @@ -566,7 +566,7 @@ func TestAccScopedToken_WithExpiresInLessThanPersistencyThreshold(t *testing.T) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: accessTokenConfig, @@ -602,7 +602,7 @@ func TestAccScopedToken_WithExpiresInSetToZeroForNonExpiringToken(t *testing.T) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: accessTokenConfig, diff --git a/pkg/artifactory/resource/user/resource_artifactory_anonymous_user_test.go b/pkg/artifactory/resource/user/resource_artifactory_anonymous_user_test.go index efc25978..c00b83b3 100644 --- a/pkg/artifactory/resource/user/resource_artifactory_anonymous_user_test.go +++ b/pkg/artifactory/resource/user/resource_artifactory_anonymous_user_test.go @@ -19,7 +19,7 @@ func TestAccAnonymousUser_Importable(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: anonymousUserConfig, @@ -45,7 +45,7 @@ func TestAccAnonymousUser_NotCreatable(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { Config: anonymousUserConfig, diff --git a/pkg/artifactory/resource/user/resource_artifactory_managed_user_test.go b/pkg/artifactory/resource/user/resource_artifactory_managed_user_test.go index 32442d7d..7b855503 100644 --- a/pkg/artifactory/resource/user/resource_artifactory_managed_user_test.go +++ b/pkg/artifactory/resource/user/resource_artifactory_managed_user_test.go @@ -161,7 +161,7 @@ func testAccManagedUserInvalidName(t *testing.T, username, errorRegex string) fu resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckUserDestroy(fqrn), Steps: []resource.TestStep{ { diff --git a/pkg/artifactory/resource/user/resource_artifactory_user_test.go b/pkg/artifactory/resource/user/resource_artifactory_user_test.go index 722927fc..4425c5d0 100644 --- a/pkg/artifactory/resource/user/resource_artifactory_user_test.go +++ b/pkg/artifactory/resource/user/resource_artifactory_user_test.go @@ -82,7 +82,7 @@ func TestAccUser_basic_groups(t *testing.T) { `, params) resource.Test(t, resource.TestCase{ - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, PreCheck: func() { acctest.PreCheck(t) }, CheckDestroy: testAccCheckManagedUserDestroy(fqrn), Steps: []resource.TestStep{ @@ -124,7 +124,7 @@ func TestAccUser_no_password(t *testing.T) { `, params) resource.Test(t, resource.TestCase{ - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, PreCheck: func() { acctest.PreCheck(t) }, CheckDestroy: testAccCheckManagedUserDestroy(fqrn), Steps: []resource.TestStep{ @@ -166,7 +166,7 @@ func TestAccUser_no_groups(t *testing.T) { `, params) resource.Test(t, resource.TestCase{ - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, PreCheck: func() { acctest.PreCheck(t) }, CheckDestroy: testAccCheckManagedUserDestroy(fqrn), Steps: []resource.TestStep{ @@ -209,7 +209,7 @@ func TestAccUser_empty_groups(t *testing.T) { `, params) resource.Test(t, resource.TestCase{ - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, PreCheck: func() { acctest.PreCheck(t) }, CheckDestroy: testAccCheckManagedUserDestroy(fqrn), Steps: []resource.TestStep{ @@ -259,7 +259,7 @@ func testAccUserInvalidName(t *testing.T, username, errorRegex string) func(t *t resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckUserDestroy(fqrn), Steps: []resource.TestStep{ { @@ -300,7 +300,7 @@ func TestAccUser_all_attributes(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckUserDestroy(fqrn), Steps: []resource.TestStep{ { @@ -372,7 +372,7 @@ func TestAccUser_PasswordNotChangeWhenOtherAttributesChangeGH340(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, - ProtoV5ProviderFactories: acctest.ProtoV5MuxProviderFactories, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckUserDestroy(fqrn), Steps: []resource.TestStep{ { From 95c3ac73c252bf5c7ec21d4bda7cc82aba2980ba Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 09:03:42 -0700 Subject: [PATCH 13/26] Switch Pipeline step into 'Matrix' type So we can parallelize acceptance test to decrease execution time Update makefile to parameterize `go test` --- .jfrog-pipelines/TFproviderTest.yml | 27 +++++++++++++++++---------- GNUmakefile | 6 +++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index d261bf87..f125e134 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -15,7 +15,8 @@ pipelines: - name: tf_provider_artifactory steps: - name: build_and_run_tf_provider - type: Bash + type: Matrix + stepMode: Bash configuration: #nodePool: default priority: 1 @@ -47,6 +48,19 @@ pipelines: SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SCANNER_HOME: $HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux SONAR_SCANNER_OPTS: "-server" + stepMultipliers: + environmentVariables: + - directory: ./pkg/artifactory/datasource + - directory: ./pkg/artifactory/datasource/repository + - directory: ./pkg/artifactory/datasource/security + - directory: ./pkg/artifactory/datasource/user + - directory: ./pkg/artifactory/provider + - directory: ./pkg/artifactory/resource/configuration + - directory: ./pkg/artifactory/resource/replication + - directory: ./pkg/artifactory/resource/repository + - directory: ./pkg/artifactory/resource/security + - directory: ./pkg/artifactory/resource/user + - directory: ./pkg/artifactory/resource/webhook execution: onStart: - echo "Sending status to GitHub." @@ -72,11 +86,8 @@ pipelines: - echo 'deb [trusted=yes] https://repo.goreleaser.com/apt/ /' | sudo tee /etc/apt/sources.list.d/goreleaser.list - sudo apt update - sudo apt install goreleaser - # TODO: enable after migration to repo21 - #- echo "Install SonarQube" - #- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip - #- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/ onExecute: + - echo "Executing matrix step on ${steplet_id}" - add_run_variables TFProviderRepo=$(echo ${res_GitHubTFProviderRepoJFrog_gitRepoRepositorySshUrl} | sed -e 's/git@/@/g' -e 's/:/\//g') - cd ${res_GitHubTFProviderRepoJFrog_resourcePath} # we need to manually move into the resource path - echo "Verify the code contents merged feature branch with master branch (detached mode)" @@ -139,11 +150,7 @@ pipelines: - add_run_variables ARTIFACTORY_ACCESS_TOKEN=${ACCESS_KEY} - echo "Unset ARTIFACTORY_PASSWORD, acceptance test will use ARTIFACTORY_ACCESS_TOKEN instead" - unset ARTIFACTORY_PASSWORD - - export TF_ACC=true - - make acceptance - # TODO: enable after migration to repo21 - #- export SONAR_TOKEN=${int_terraform_artifactory_sonarqube_token} - #- make scan + - TF_ACC=true make acceptance TEST=${directory} - make install onSuccess: - echo "Success" diff --git a/GNUmakefile b/GNUmakefile index de67bd32..7616841a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -TEST?=./... +TEST?=./pkg/... PRODUCT=artifactory GO_ARCH=$(shell go env GOARCH) TARGET_ARCH=$(shell go env GOOS)_${GO_ARCH} @@ -52,11 +52,11 @@ attach: smoke: fmt export TF_ACC=true && \ - go test -run '${SMOKE_TESTS}' -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_PROVIDER_VERSION}-test'" -v -p 1 -timeout 5m ./pkg/... -count=1 + go test -run '${SMOKE_TESTS}' -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_PROVIDER_VERSION}-test'" -v -p 1 -timeout 5m $(TEST). -count=1 acceptance: fmt export TF_ACC=true && \ - go test -cover -coverprofile=coverage.txt -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_PROVIDER_VERSION}-test'" -v -p 1 -parallel 20 -timeout 1h ./pkg/... + go test -cover -coverprofile=coverage.txt -ldflags="-X '${PKG_VERSION_PATH}.Version=${NEXT_PROVIDER_VERSION}-test'" -v -p 1 -parallel 20 -timeout 1h $(TEST) coverage: go tool cover -html=coverage.txt From 801cb246f3dc77e25d42a992f81437bbfc5438a8 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 09:54:10 -0700 Subject: [PATCH 14/26] Update TFproviderTest.yml Rename step name temporarily so Pipelines can sync without error. --- .jfrog-pipelines/TFproviderTest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index f125e134..e741698d 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -12,7 +12,7 @@ resources: cancelPendingRunsOn: pullRequestUpdate: false pipelines: - - name: tf_provider_artifactory + - name: tf_provider_artifactory_matrix steps: - name: build_and_run_tf_provider type: Matrix From 1998c341fda65cf87addc62cb66153684e01ba9b Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 09:55:21 -0700 Subject: [PATCH 15/26] Update TFproviderTest.yml Renamed the wrong thing last commit --- .jfrog-pipelines/TFproviderTest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index e741698d..28dbba34 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -12,9 +12,9 @@ resources: cancelPendingRunsOn: pullRequestUpdate: false pipelines: - - name: tf_provider_artifactory_matrix + - name: tf_provider_artifactory steps: - - name: build_and_run_tf_provider + - name: build_and_run_tf_provider_matrix type: Matrix stepMode: Bash configuration: From eb2a3fb8cef9ba8ce94ac06003480095efb7e8ae Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 09:57:54 -0700 Subject: [PATCH 16/26] Update TFproviderTest.yml Fix incorrect matrix step attribute name. Update input step name to match --- .jfrog-pipelines/TFproviderTest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 28dbba34..246ecafb 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -48,7 +48,7 @@ pipelines: SONAR_SCANNER_VERSION: 4.7.0.2747 SONAR_SCANNER_HOME: $HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux SONAR_SCANNER_OPTS: "-server" - stepMultipliers: + stepletMultipliers: environmentVariables: - directory: ./pkg/artifactory/datasource - directory: ./pkg/artifactory/datasource/repository @@ -174,7 +174,7 @@ pipelines: - name: partnership_slack - name: partnership_github inputSteps: - - name: build_and_run_tf_provider + - name: build_and_run_tf_provider_matrix inputResources: - name: GitHubTFProviderRepoJFrog execution: From 3ce8a58fe7915425a6888bf8ef104572507e9218 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 10:27:43 -0700 Subject: [PATCH 17/26] Update TFproviderTest.yml Add steplet id to container name --- .jfrog-pipelines/TFproviderTest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 246ecafb..793514bf 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -114,7 +114,7 @@ pipelines: add_run_variables ARTIFACTORY_VERSION=${ARTIFACTORY_TEST_VERSION} echo "Artifactory version: "${ARTIFACTORY_VERSION} fi - - export ARTIFACTORY_CONTAINER_NAME=artifactory + - export ARTIFACTORY_CONTAINER_NAME=artifactory-${steplet_id} - >- docker run -i --name ${ARTIFACTORY_CONTAINER_NAME} -t -d --rm -v "${res_GitHubTFProviderRepoJFrog_resourcePath}/scripts/artifactory.lic:/artifactory_extra_conf/artifactory.lic:ro" \ -p8081:8081 -p8082:8082 -p8080:8080 releases-docker.jfrog.io/jfrog/artifactory-pro:${ARTIFACTORY_VERSION} From 851a1a1873142e08794f529766f7407f8634df1b Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 10:52:58 -0700 Subject: [PATCH 18/26] Update TFproviderTest.yml Use Pipeline steplet ID to create unique port number for each Docker container --- .jfrog-pipelines/TFproviderTest.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 793514bf..47fb171d 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -115,25 +115,26 @@ pipelines: echo "Artifactory version: "${ARTIFACTORY_VERSION} fi - export ARTIFACTORY_CONTAINER_NAME=artifactory-${steplet_id} + - export ARTIFACTORY_PORT=$(expr 8082 + ${steplet_id}) - >- docker run -i --name ${ARTIFACTORY_CONTAINER_NAME} -t -d --rm -v "${res_GitHubTFProviderRepoJFrog_resourcePath}/scripts/artifactory.lic:/artifactory_extra_conf/artifactory.lic:ro" \ - -p8081:8081 -p8082:8082 -p8080:8080 releases-docker.jfrog.io/jfrog/artifactory-pro:${ARTIFACTORY_VERSION} + -p ${ARTIFACTORY_PORT}:8082 releases-docker.jfrog.io/jfrog/artifactory-pro:${ARTIFACTORY_VERSION} - echo "Set localhost to a container IP address, since we run docker inside of docker" - export LOCALHOST=$(docker inspect -f '{{`{{range.NetworkSettings.Networks}}{{.Gateway}}{{end}}`}}' ${ARTIFACTORY_CONTAINER_NAME}) - echo "Using ${LOCALHOST} as 'localhost' ip address" - echo "Waiting for Artifactory to start (doesn't reflect the start of the UI!)" + - export ARTIFACTORY_URL="http://${LOCALHOST}:${ARTIFACTORY_PORT}" - >- - until curl -sf -u admin:password http://${LOCALHOST}:8081/artifactory/api/system/licenses/; do + until curl -sf -u admin:password http://${ARTIFACTORY_URL}/artifactory/api/system/licenses/; do printf '.' sleep 4 done - echo "Add variables needed to run Terraform Provider" - - export ARTIFACTORY_URL="http://${LOCALHOST}:8082" - export ARTIFACTORY_USERNAME=admin - export ARTIFACTORY_PASSWORD=password - echo "Get cookie to generate Access token. We need a pause to let UI come up to get cookies" - sleep 180 - - sudo curl http://${LOCALHOST}:8082/router/api/v1/system/health + - sudo curl http://${ARTIFACTORY_URL}/router/api/v1/system/health - >- export COOKIES=$(curl -c - "${ARTIFACTORY_URL}/ui/api/v1/ui/auth/login?_spring_security_remember_me=false" \ --header "accept: application/json, text/plain, */*" \ From a2e8ae9baeace31656590df2adf9a2fc28350f0d Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 11:22:17 -0700 Subject: [PATCH 19/26] Update TFproviderTest.yml Run matrix step as multi-node step. Revert docker container name and port mapping --- .jfrog-pipelines/TFproviderTest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 47fb171d..c18c5cec 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -19,6 +19,7 @@ pipelines: stepMode: Bash configuration: #nodePool: default + multiNode: true priority: 1 timeoutSeconds: 3600 # 60 minutes runtime: @@ -114,16 +115,15 @@ pipelines: add_run_variables ARTIFACTORY_VERSION=${ARTIFACTORY_TEST_VERSION} echo "Artifactory version: "${ARTIFACTORY_VERSION} fi - - export ARTIFACTORY_CONTAINER_NAME=artifactory-${steplet_id} - - export ARTIFACTORY_PORT=$(expr 8082 + ${steplet_id}) + - export ARTIFACTORY_CONTAINER_NAME=artifactory - >- docker run -i --name ${ARTIFACTORY_CONTAINER_NAME} -t -d --rm -v "${res_GitHubTFProviderRepoJFrog_resourcePath}/scripts/artifactory.lic:/artifactory_extra_conf/artifactory.lic:ro" \ - -p ${ARTIFACTORY_PORT}:8082 releases-docker.jfrog.io/jfrog/artifactory-pro:${ARTIFACTORY_VERSION} + -p 8082:8082 releases-docker.jfrog.io/jfrog/artifactory-pro:${ARTIFACTORY_VERSION} - echo "Set localhost to a container IP address, since we run docker inside of docker" - export LOCALHOST=$(docker inspect -f '{{`{{range.NetworkSettings.Networks}}{{.Gateway}}{{end}}`}}' ${ARTIFACTORY_CONTAINER_NAME}) - echo "Using ${LOCALHOST} as 'localhost' ip address" - echo "Waiting for Artifactory to start (doesn't reflect the start of the UI!)" - - export ARTIFACTORY_URL="http://${LOCALHOST}:${ARTIFACTORY_PORT}" + - export ARTIFACTORY_URL="http://${LOCALHOST}:8082" - >- until curl -sf -u admin:password http://${ARTIFACTORY_URL}/artifactory/api/system/licenses/; do printf '.' From 89c7519768f851dfe22430aab2772f6e117464de Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 11:39:13 -0700 Subject: [PATCH 20/26] Update TFproviderTest.yml Fix incorrect url for curls --- .jfrog-pipelines/TFproviderTest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index c18c5cec..91385ecb 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -125,7 +125,7 @@ pipelines: - echo "Waiting for Artifactory to start (doesn't reflect the start of the UI!)" - export ARTIFACTORY_URL="http://${LOCALHOST}:8082" - >- - until curl -sf -u admin:password http://${ARTIFACTORY_URL}/artifactory/api/system/licenses/; do + until curl -sf -u admin:password ${ARTIFACTORY_URL}/artifactory/api/system/licenses/; do printf '.' sleep 4 done @@ -134,7 +134,7 @@ pipelines: - export ARTIFACTORY_PASSWORD=password - echo "Get cookie to generate Access token. We need a pause to let UI come up to get cookies" - sleep 180 - - sudo curl http://${ARTIFACTORY_URL}/router/api/v1/system/health + - sudo curl ${ARTIFACTORY_URL}/router/api/v1/system/health - >- export COOKIES=$(curl -c - "${ARTIFACTORY_URL}/ui/api/v1/ui/auth/login?_spring_security_remember_me=false" \ --header "accept: application/json, text/plain, */*" \ From 194d095b5da5909cf3b20d0a7efb2d21fe71bb6b Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 11:49:14 -0700 Subject: [PATCH 21/26] Update TFproviderTest.yml Add go test wildcard for repository directories. --- .jfrog-pipelines/TFproviderTest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 91385ecb..96cbc09f 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -52,13 +52,13 @@ pipelines: stepletMultipliers: environmentVariables: - directory: ./pkg/artifactory/datasource - - directory: ./pkg/artifactory/datasource/repository + - directory: ./pkg/artifactory/datasource/repository/... - directory: ./pkg/artifactory/datasource/security - directory: ./pkg/artifactory/datasource/user - directory: ./pkg/artifactory/provider - directory: ./pkg/artifactory/resource/configuration - directory: ./pkg/artifactory/resource/replication - - directory: ./pkg/artifactory/resource/repository + - directory: ./pkg/artifactory/resource/repository/... - directory: ./pkg/artifactory/resource/security - directory: ./pkg/artifactory/resource/user - directory: ./pkg/artifactory/resource/webhook From a398edf2975774977570d2df17e1be14620ff033 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 11:58:16 -0700 Subject: [PATCH 22/26] Update TFproviderTest.yml Check UI login URL instead of hardcoded waiting for 3 min --- .jfrog-pipelines/TFproviderTest.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 96cbc09f..cf9c2013 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -133,7 +133,11 @@ pipelines: - export ARTIFACTORY_USERNAME=admin - export ARTIFACTORY_PASSWORD=password - echo "Get cookie to generate Access token. We need a pause to let UI come up to get cookies" - - sleep 180 + - >- + until curl -sf -u admin:password ${ARTIFACTORY_URL}/ui/login/; do + printf '.' + sleep 4 + done - sudo curl ${ARTIFACTORY_URL}/router/api/v1/system/health - >- export COOKIES=$(curl -c - "${ARTIFACTORY_URL}/ui/api/v1/ui/auth/login?_spring_security_remember_me=false" \ From 30c334c4f17df80b9f84b11f86f441d6ef2afbae Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 12:11:55 -0700 Subject: [PATCH 23/26] Update TFproviderTest.yml Break up repository testing into even finer groups to get more parallelization --- .jfrog-pipelines/TFproviderTest.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index cf9c2013..4811bdb7 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -52,13 +52,19 @@ pipelines: stepletMultipliers: environmentVariables: - directory: ./pkg/artifactory/datasource - - directory: ./pkg/artifactory/datasource/repository/... + - directory: ./pkg/artifactory/datasource/repository/federated + - directory: ./pkg/artifactory/datasource/repository/local + - directory: ./pkg/artifactory/datasource/repository/remote + - directory: ./pkg/artifactory/datasource/repository/virtual - directory: ./pkg/artifactory/datasource/security - directory: ./pkg/artifactory/datasource/user - directory: ./pkg/artifactory/provider - directory: ./pkg/artifactory/resource/configuration - directory: ./pkg/artifactory/resource/replication - - directory: ./pkg/artifactory/resource/repository/... + - directory: ./pkg/artifactory/resource/repository/federated + - directory: ./pkg/artifactory/resource/repository/local + - directory: ./pkg/artifactory/resource/repository/remote + - directory: ./pkg/artifactory/resource/repository/virtual - directory: ./pkg/artifactory/resource/security - directory: ./pkg/artifactory/resource/user - directory: ./pkg/artifactory/resource/webhook From d813096d5515ef7a6059bbe69c8a5980d7acf974 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Fri, 6 Oct 2023 12:21:52 -0700 Subject: [PATCH 24/26] Update TFproviderTest.yml Improve Slack message after matrix step is completed --- .jfrog-pipelines/TFproviderTest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.jfrog-pipelines/TFproviderTest.yml b/.jfrog-pipelines/TFproviderTest.yml index 4811bdb7..efc48a4a 100644 --- a/.jfrog-pipelines/TFproviderTest.yml +++ b/.jfrog-pipelines/TFproviderTest.yml @@ -165,14 +165,14 @@ pipelines: - make install onSuccess: - echo "Success" - - send_notification partnership_slack --text "${pipeline_name} step <${step_url}|${step_name}> is completed. Version ${PROVIDER_VERSION:-" wasn't set"}." + - send_notification partnership_slack --text "${pipeline_name} step <${step_url}|${step_name} - ${directory}> is completed. Version ${PROVIDER_VERSION:-" wasn't set"}." onFailure: - echo "Failure, sending status to GitHub and Slack." - export STATE="failure" - export DESCRIPTION="Pipeline has failed." - git clone https://${int_partnership_github_token}@github.com/jfrog/terraform-provider-shared.git - ./terraform-provider-shared/scripts/github-status.sh ${res_GitHubTFProviderRepoJFrog_gitProvider_token} ${res_GitHubTFProviderRepoJFrog_gitRepoFullName} ${res_GitHubTFProviderRepoJFrog_commitSha} - - send_notification partnership_slack --text "${pipeline_name} pipeline failed on <${step_url}|${step_name}> step" + - send_notification partnership_slack --text "${pipeline_name} pipeline failed on <${step_url}|${step_name} - ${directory}> step" onComplete: - echo "Complete" From 7767125b6e0cad29c4abd7d4ed10d37d9dd17403 Mon Sep 17 00:00:00 2001 From: JFrog CI Date: Fri, 6 Oct 2023 19:35:52 +0000 Subject: [PATCH 25/26] JFrog Pipelines - Add Artifactory version to CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f809ad45..4dc3771f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 9.3.1 (Oct 6, 2023) +## 9.3.1 (Oct 6, 2023). Tested on Artifactory 7.68.13 with Terraform CLI v1.6.0 BUG FIX: * resource/artifactory_scoped_token: Remove default value for `expires_in` attribute which should fix state drift when upgrading from 7.11.2 or earlier. Issue: [#818](https://github.com/jfrog/terraform-provider-artifactory/issues/818) PR: [#820](https://github.com/jfrog/terraform-provider-artifactory/pull/820) From 7f2ef76089aaa1b317d8bd5739bd5b9db08f3206 Mon Sep 17 00:00:00 2001 From: JFrog CI Date: Fri, 6 Oct 2023 19:54:56 +0000 Subject: [PATCH 26/26] JFrog Pipelines - Add Artifactory version to CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5b7251..3e63bf47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 9.4.0 (Oct 5, 2023) +## 9.4.0 (Oct 5, 2023). Tested on Artifactory 7.68.13 with Terraform CLI v1.6.0 FEATURES: