diff --git a/CHANGELOG.md b/CHANGELOG.md index ad9691739..dc341eff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 9.5.1 (Oct 12, 2023). Tested on Artifactory 7.68.14 with Terraform CLI v1.6.1 + +SECURITY: + +* provider: Bump golang.org/x/net from 0.11.0 to 0.17.0 PR: [#824](https://github.com/jfrog/terraform-provider-artifactory/pull/824) + +## 9.5.0 (Oct 11, 2023). Tested on Artifactory 7.68.14 with Terraform CLI v1.6.1 + +FEATURES: + +* resource/artifactory_local_huggingfaceml_repository, resource/artifactory_remote_huggingfaceml_repository: add new local and remote resources for managing Hugging Face repository. PR: [#823](https://github.com/jfrog/terraform-provider-artifactory/pull/823) + ## 9.4.0 (Oct 5, 2023). Tested on Artifactory 7.68.13 with Terraform CLI v1.6.0 FEATURES: diff --git a/docs/resources/local_huggingfaceml_repository.md b/docs/resources/local_huggingfaceml_repository.md new file mode 100644 index 000000000..4892b2b26 --- /dev/null +++ b/docs/resources/local_huggingfaceml_repository.md @@ -0,0 +1,33 @@ +--- +subcategory: "Local Repositories" +--- +# Artifactory Local Hugging Face Repository Resource + +Creates a local Hugging Face repository. + +Official documentation can be found [here](https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-local-hugging-face-repositories). + +## Example Usage + +```hcl +resource "artifactory_local_huggingfaceml_repository" "local-huggingfaceml-repo" { + key = "local-huggingfaceml-repo" +} +``` + +## Argument Reference + +Arguments have a one to one mapping with the [JFrog API](https://www.jfrog.com/confluence/display/RTF/Repository+Configuration+JSON). + +The following arguments are supported, along with the [common list of arguments for the local repositories](local.md): + +* `key` - (Required) the identity key of the repo. +* `description` - (Optional) +* `notes` - (Optional) + +## Import + +Local repositories can be imported using their name, e.g. +``` +$ terraform import artifactory_local_huggingfaceml_repository.local-huggingfaceml-repo local-huggingfaceml-repo +``` diff --git a/docs/resources/remote.md b/docs/resources/remote.md index 1feb3edff..10e3e4c03 100644 --- a/docs/resources/remote.md +++ b/docs/resources/remote.md @@ -10,8 +10,7 @@ repository-specific arguments, listed in separate repository-specific documents. Passwords can only be used when encryption is turned off, according to [Key Encryption instruction](https://www.jfrog.com/confluence/display/RTF/Artifactory+Key+Encryption). Since only the artifactory server can decrypt them it is impossible for terraform to diff changes correctly. -To get full management, passwords can be decrypted globally using `POST /api/system/decrypt`. If this is not possible, -the password diff can be disabled per resource with-- noting that this will require resources to be tainted for an update: +To get full management, passwords can be decrypted globally using `POST /api/system/decrypt`. If this is not possible, the password diff can be disabled per resource with `lifecycle.ignore_changes` -- noting that this will require resources to be tainted for an update: ```hcl lifecycle { @@ -82,3 +81,4 @@ All generic repo arguments are supported, in addition to: the artifact directly from the cloud storage provider. Available in Enterprise+ and Edge licenses only. * `cdn_redirect` - (Optional) When set, download requests to this repository will redirect the client to download the artifact directly from AWS CloudFront. Available in Enterprise+ and Edge licenses only. +* `disable_url_normalization` - (Optional) Whether to disable URL normalization, default is `false`. diff --git a/docs/resources/remote_docker_repository.md b/docs/resources/remote_docker_repository.md index 193b7c8d1..ddc16e301 100644 --- a/docs/resources/remote_docker_repository.md +++ b/docs/resources/remote_docker_repository.md @@ -17,7 +17,6 @@ resource "artifactory_remote_docker_repository" "my-remote-docker" { enable_token_authentication = true url = "https://registry-1.docker.io/" block_pushing_schema1 = true - disable_url_normalization = true } ``` @@ -43,7 +42,6 @@ The following arguments are supported, along with the [common list of arguments This value `[**]` must be assigned to the attribute manually, if user don't specify any other non-default values. We don't want to make this attribute required, but it must be set to avoid the state drift on update. Note: Artifactory assigns `[**]` on update if HCL doesn't have the attribute set or the list is empty. -* `disable_url_normalization` - (Optional) Whether to disable URL normalization, default is `false`. ## Import diff --git a/docs/resources/remote_huggingfaceml_repository.md b/docs/resources/remote_huggingfaceml_repository.md new file mode 100644 index 000000000..3eb26adfb --- /dev/null +++ b/docs/resources/remote_huggingfaceml_repository.md @@ -0,0 +1,34 @@ +--- +subcategory: "Remote Repositories" +--- +# Artifactory Remote Hugging Face Repository Resource + +Provides a remote Hugging Face repository. + +Official documentation can be found [here](https://jfrog.com/help/r/jfrog-artifactory-documentation/set-up-remote-hugging-face-repositories). + +## Example Usage + +```hcl +resource "artifactory_remote_huggingfaceml_repository" "huggingfaceml-remote" { + key = "huggingfaceml-remote-foo25" +} +``` + +## Argument Reference + +Arguments have a one to one mapping with the [JFrog API](https://www.jfrog.com/confluence/display/RTF/Repository+Configuration+JSON). + +The following arguments are supported, along with the [common list of arguments for the remote repositories](remote.md): + +* `key` - (Required) A mandatory identifier for the repository that must be unique. It cannot begin with a number or + contain spaces or special characters. +* `description` - (Optional) +* `notes` - (Optional) + +## Import + +Remote repositories can be imported using their name, e.g. +``` +$ terraform import artifactory_remote_huggingfaceml_repository.huggingfaceml-remote huggingfaceml-remote +``` diff --git a/go.mod b/go.mod index b49b32596..84219a42b 100644 --- a/go.mod +++ b/go.mod @@ -20,8 +20,8 @@ require ( github.com/sethvargo/go-password v0.2.0 github.com/stretchr/testify v1.7.2 golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 - golang.org/x/net v0.11.0 - golang.org/x/text v0.12.0 + golang.org/x/net v0.17.0 + golang.org/x/text v0.13.0 gopkg.in/ldap.v2 v2.5.1 gopkg.in/yaml.v3 v3.0.1 ) @@ -77,9 +77,9 @@ require ( github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.13.3 // indirect - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/sys v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.56.1 // indirect diff --git a/go.sum b/go.sum index dbaa09d91..ccd04d305 100644 --- a/go.sum +++ b/go.sum @@ -192,8 +192,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819 h1:EDuYyU/MkFXllv9QF9819VlI9a4tzGuCbhG0ExK9o1U= golang.org/x/exp v0.0.0-20230809150735-7b3493d9a819/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= @@ -202,8 +202,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -217,15 +217,15 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/pkg/artifactory/provider/resources.go b/pkg/artifactory/provider/resources.go index 888215b4b..04ea09b6b 100644 --- a/pkg/artifactory/provider/resources.go +++ b/pkg/artifactory/provider/resources.go @@ -52,6 +52,7 @@ func resourcesMap() map[string]*schema.Resource { "artifactory_remote_generic_repository": remote.ResourceArtifactoryRemoteGenericRepository(), "artifactory_remote_go_repository": remote.ResourceArtifactoryRemoteGoRepository(), "artifactory_remote_helm_repository": remote.ResourceArtifactoryRemoteHelmRepository(), + "artifactory_remote_huggingfaceml_repository": remote.ResourceArtifactoryRemoteHuggingFaceRepository(), "artifactory_remote_maven_repository": remote.ResourceArtifactoryRemoteMavenRepository(), "artifactory_remote_nuget_repository": remote.ResourceArtifactoryRemoteNugetRepository(), "artifactory_remote_pypi_repository": remote.ResourceArtifactoryRemotePypiRepository(), diff --git a/pkg/artifactory/resource/repository/default_repo_layout_map.go b/pkg/artifactory/resource/repository/default_repo_layout_map.go index 29bf8841c..30adb41a3 100644 --- a/pkg/artifactory/resource/repository/default_repo_layout_map.go +++ b/pkg/artifactory/resource/repository/default_repo_layout_map.go @@ -147,9 +147,19 @@ var defaultRepoLayoutMap = map[string]SupportedRepoClasses{ "helm": { RepoLayoutRef: "simple-default", SupportedRepoTypes: map[string]bool{ - "local": true, - "remote": true, - "virtual": true, "federated": true, + "local": true, + "remote": true, + "virtual": true, + "federated": true, + }, + }, + "huggingfaceml": { + RepoLayoutRef: "simple-default", + SupportedRepoTypes: map[string]bool{ + "local": true, + "remote": true, + "virtual": false, + "federated": false, }, }, "ivy": { diff --git a/pkg/artifactory/resource/repository/local/local.go b/pkg/artifactory/resource/repository/local/local.go index 5afe304eb..0d0391952 100644 --- a/pkg/artifactory/resource/repository/local/local.go +++ b/pkg/artifactory/resource/repository/local/local.go @@ -22,6 +22,7 @@ var PackageTypesLikeGeneric = []string{ "gitlfs", "go", "helm", + "huggingfaceml", "npm", "opkg", "pub", @@ -60,18 +61,6 @@ func (bp RepositoryBaseParams) Id() string { var BaseLocalRepoSchema = utilsdk.MergeMaps( repository.BaseRepoSchema, map[string]*schema.Schema{ - "includes_pattern": { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "List of artifact patterns to include when evaluating artifact requests in the form of x/y/**/z/*. When used, only artifacts matching one of the include patterns are served. By default, all artifacts are included (**/*).", - }, - "excludes_pattern": { - Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "List of artifact patterns to exclude when evaluating artifact requests, in the form of x/y/**/z/*. By default no artifacts are excluded.", - }, "blacked_out": { Type: schema.TypeBool, Optional: true, diff --git a/pkg/artifactory/resource/repository/local/resource_artifactory_local_repository_test.go b/pkg/artifactory/resource/repository/local/resource_artifactory_local_repository_test.go index 1062ec357..1bce50d79 100644 --- a/pkg/artifactory/resource/repository/local/resource_artifactory_local_repository_test.go +++ b/pkg/artifactory/resource/repository/local/resource_artifactory_local_repository_test.go @@ -4,6 +4,7 @@ import ( "fmt" "math/rand" "regexp" + "strconv" "strings" "testing" "time" @@ -521,6 +522,56 @@ func TestAccLocalDockerV2RepositoryWithDefaultMaxUniqueTagsGH370(t *testing.T) { }) } +func TestAccLocalHuggingFaceMLRepository(t *testing.T) { + _, fqrn, name := testutil.MkNames("huggingfaceml-local", "artifactory_local_huggingfaceml_repository") + + params := map[string]interface{}{ + "name": name, + "blacked_out": testutil.RandBool(), + "xray_index": testutil.RandBool(), + "property_set": "artifactory", + "archive_browsing_enabled": testutil.RandBool(), + } + localRepositoryBasic := utilsdk.ExecuteTemplate("TestAccLocalHuggingFaceMLRepository", ` + resource "artifactory_local_huggingfaceml_repository" "{{ .name }}" { + key = "{{ .name }}" + blacked_out = {{ .blacked_out }} + xray_index = {{ .xray_index }} + property_sets = ["{{ .property_set }}"] + archive_browsing_enabled = {{ .archive_browsing_enabled }} + } + `, params) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckRepo), + Steps: []resource.TestStep{ + { + Config: localRepositoryBasic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "key", name), + resource.TestCheckResourceAttr(fqrn, "blacked_out", strconv.FormatBool(params["blacked_out"].(bool))), + resource.TestCheckResourceAttr(fqrn, "xray_index", strconv.FormatBool(params["xray_index"].(bool))), + resource.TestCheckResourceAttr(fqrn, "property_sets.#", "1"), + resource.TestCheckResourceAttr(fqrn, "property_sets.0", params["property_set"].(string)), + resource.TestCheckResourceAttr(fqrn, "archive_browsing_enabled", strconv.FormatBool(params["archive_browsing_enabled"].(bool))), + resource.TestCheckResourceAttr(fqrn, "repo_layout_ref", func() string { + r, _ := repository.GetDefaultRepoLayoutRef("local", "huggingfaceml")() + return r.(string) + }()), //Check to ensure repository layout is set as per default even when it is not passed. + ), + }, + { + ResourceName: fqrn, + ImportState: true, + ImportStateVerify: true, + ImportStateCheck: validator.CheckImportState(name, "key"), + }, + }, + }) +} + func TestAccLocalNugetRepository(t *testing.T) { _, fqrn, name := testutil.MkNames("nuget-local", "artifactory_local_nuget_repository") params := map[string]interface{}{ @@ -695,11 +746,13 @@ func TestAccLocalGenericRepository(t *testing.T) { params := map[string]interface{}{ "name": name, "priority_resolution": testutil.RandBool(), + "property_set": "artifactory", } localRepositoryBasic := utilsdk.ExecuteTemplate("TestAccLocalGenericRepository", ` resource "artifactory_local_generic_repository" "{{ .name }}" { key = "{{ .name }}" priority_resolution = "{{ .priority_resolution }}" + property_sets = ["{{ .property_set }}"] } `, params) @@ -713,6 +766,8 @@ func TestAccLocalGenericRepository(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(fqrn, "key", name), resource.TestCheckResourceAttr(fqrn, "priority_resolution", fmt.Sprintf("%t", params["priority_resolution"])), + resource.TestCheckResourceAttr(fqrn, "property_sets.#", "1"), + resource.TestCheckResourceAttr(fqrn, "property_sets.0", params["property_set"].(string)), ), }, { @@ -845,36 +900,38 @@ func TestAccLocalNpmRepository(t *testing.T) { }) } -func mkTestCase(repoType string, t *testing.T) (*testing.T, resource.TestCase) { - name := fmt.Sprintf("terraform-local-%s-%d-full", repoType, rand.Int()) - resourceName := fmt.Sprintf("artifactory_local_%s_repository.%s", repoType, name) +func mkTestCase(packageType string, t *testing.T) (*testing.T, resource.TestCase) { + name := fmt.Sprintf("local-%s-%d-full", packageType, rand.Int()) + resourceName := fmt.Sprintf("artifactory_local_%s_repository.%s", packageType, name) xrayIndex := testutil.RandBool() - fqrn := fmt.Sprintf("artifactory_local_%s_repository.%s", repoType, name) + fqrn := fmt.Sprintf("artifactory_local_%s_repository.%s", packageType, name) params := map[string]interface{}{ - "repoType": repoType, - "name": name, - "xrayIndex": xrayIndex, - "cdnRedirect": false, // even when set to true, it comes back as false on the wire (presumably unless testing against a cloud platform) - + "packageType": packageType, + "name": name, + "xrayIndex": xrayIndex, + "cdnRedirect": false, // even when set to true, it comes back as false on the wire (presumably unless testing against a cloud platform) + "property_set": "artifactory", } cfg := utilsdk.ExecuteTemplate("TestAccLocalRepository", ` - resource "artifactory_local_{{ .repoType }}_repository" "{{ .name }}" { + resource "artifactory_local_{{ .packageType }}_repository" "{{ .name }}" { key = "{{ .name }}" description = "Test repo for {{ .name }}" notes = "Test repo for {{ .name }}" xray_index = {{ .xrayIndex }} cdn_redirect = {{ .cdnRedirect }} + property_sets = ["{{ .property_set }}"] } `, params) updatedCfg := utilsdk.ExecuteTemplate("TestAccLocalRepository", ` - resource "artifactory_local_{{ .repoType }}_repository" "{{ .name }}" { + resource "artifactory_local_{{ .packageType }}_repository" "{{ .name }}" { key = "{{ .name }}" description = "" notes = "" xray_index = {{ .xrayIndex }} cdn_redirect = {{ .cdnRedirect }} + property_sets = ["{{ .property_set }}"] } `, params) @@ -887,22 +944,24 @@ func mkTestCase(repoType string, t *testing.T) (*testing.T, resource.TestCase) { Config: cfg, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "key", name), - resource.TestCheckResourceAttr(resourceName, "package_type", repoType), + resource.TestCheckResourceAttr(resourceName, "package_type", packageType), resource.TestCheckResourceAttr(resourceName, "description", fmt.Sprintf("Test repo for %s", name)), resource.TestCheckResourceAttr(resourceName, "notes", fmt.Sprintf("Test repo for %s", name)), - resource.TestCheckResourceAttr(resourceName, "repo_layout_ref", func() string { r, _ := repository.GetDefaultRepoLayoutRef("local", repoType)(); return r.(string) }()), //Check to ensure repository layout is set as per default even when it is not passed. + resource.TestCheckResourceAttr(resourceName, "repo_layout_ref", func() string { r, _ := repository.GetDefaultRepoLayoutRef("local", packageType)(); return r.(string) }()), //Check to ensure repository layout is set as per default even when it is not passed. resource.TestCheckResourceAttr(resourceName, "xray_index", fmt.Sprintf("%t", xrayIndex)), resource.TestCheckResourceAttr(resourceName, "cdn_redirect", fmt.Sprintf("%t", params["cdnRedirect"])), + resource.TestCheckResourceAttr(resourceName, "property_sets.#", "1"), + resource.TestCheckResourceAttr(resourceName, "property_sets.0", params["property_set"].(string)), ), }, { Config: updatedCfg, Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "key", name), - resource.TestCheckResourceAttr(resourceName, "package_type", repoType), + resource.TestCheckResourceAttr(resourceName, "package_type", packageType), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "notes", ""), - resource.TestCheckResourceAttr(resourceName, "repo_layout_ref", func() string { r, _ := repository.GetDefaultRepoLayoutRef("local", repoType)(); return r.(string) }()), //Check to ensure repository layout is set as per default even when it is not passed. + resource.TestCheckResourceAttr(resourceName, "repo_layout_ref", func() string { r, _ := repository.GetDefaultRepoLayoutRef("local", packageType)(); return r.(string) }()), //Check to ensure repository layout is set as per default even when it is not passed. resource.TestCheckResourceAttr(resourceName, "xray_index", fmt.Sprintf("%t", xrayIndex)), resource.TestCheckResourceAttr(resourceName, "cdn_redirect", fmt.Sprintf("%t", params["cdnRedirect"])), ), @@ -917,10 +976,10 @@ func mkTestCase(repoType string, t *testing.T) (*testing.T, resource.TestCase) { } } -func TestAccLocalAllRepoTypes(t *testing.T) { - for _, repo := range local.PackageTypesLikeGeneric { - t.Run(repo, func(t *testing.T) { - resource.Test(mkTestCase(repo, t)) +func TestAccLocalAllPackageTypes(t *testing.T) { + for _, packageType := range local.PackageTypesLikeGeneric { + t.Run(packageType, func(t *testing.T) { + resource.Test(mkTestCase(packageType, t)) }) } } diff --git a/pkg/artifactory/resource/repository/remote/remote.go b/pkg/artifactory/resource/repository/remote/remote.go index a6308c794..036a53f72 100644 --- a/pkg/artifactory/resource/repository/remote/remote.go +++ b/pkg/artifactory/resource/repository/remote/remote.go @@ -60,6 +60,7 @@ type RepositoryRemoteBaseParams struct { ListRemoteFolderItems bool `json:"listRemoteFolderItems"` DownloadRedirect bool `hcl:"download_direct" json:"downloadRedirect,omitempty"` CdnRedirect bool `json:"cdnRedirect"` + DisableURLNormalization bool `hcl:"disable_url_normalization" json:"disableUrlNormalization"` } func (r RepositoryRemoteBaseParams) GetRclass() string { @@ -369,6 +370,12 @@ var BaseRemoteRepoSchema = func(isResource bool) map[string]*schema.Schema { Default: false, Description: "When set, download requests to this repository will redirect the client to download the artifact directly from AWS CloudFront. Available in Enterprise+ and Edge licenses only. Default value is 'false'", }, + "disable_url_normalization": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to disable URL normalization, default is `false`.", + }, }, ) } @@ -506,6 +513,7 @@ func UnpackBaseRemoteRepo(s *schema.ResourceData, packageType string) Repository PriorityResolution: d.GetBool("priority_resolution", false), ListRemoteFolderItems: d.GetBool("list_remote_folder_items", false), MismatchingMimeTypeOverrideList: d.GetString("mismatching_mime_types_override_list", false), + DisableURLNormalization: d.GetBool("disable_url_normalization", false), } if v, ok := d.GetOk("content_synchronisation"); ok { contentSynchronisationConfig := v.([]interface{})[0].(map[string]interface{}) diff --git a/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_docker_repository.go b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_docker_repository.go index 69bc7d81e..a4f5c4725 100644 --- a/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_docker_repository.go +++ b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_docker_repository.go @@ -47,12 +47,6 @@ var DockerRemoteSchema = func(isResource bool) map[string]*schema.Schema { "This value must be assigned to the attribute manually, if user don't specify any other non-default values." + "This attribute must be set together with `external_dependencies_enabled = true`", }, - "disable_url_normalization": { - Type: schema.TypeBool, - Optional: true, - Default: false, - Description: "Whether to disable URL normalization, default is `false`.", - }, }, repository.RepoLayoutRefSchema(rclass, DockerPackageType), ) @@ -64,7 +58,6 @@ type DockerRemoteRepo struct { ExternalDependenciesPatterns []string `json:"externalDependenciesPatterns,omitempty"` EnableTokenAuthentication bool `json:"enableTokenAuthentication"` BlockPushingSchema1 bool `hcl:"block_pushing_schema1" json:"blockPushingSchema1"` - DisableURLNormalization bool `hcl:"disable_url_normalization" json:"disableUrlNormalization"` } func ResourceArtifactoryRemoteDockerRepository() *schema.Resource { @@ -76,7 +69,6 @@ func ResourceArtifactoryRemoteDockerRepository() *schema.Resource { ExternalDependenciesEnabled: d.GetBool("external_dependencies_enabled", false), BlockPushingSchema1: d.GetBool("block_pushing_schema1", false), ExternalDependenciesPatterns: d.GetList("external_dependencies_patterns"), - DisableURLNormalization: d.GetBool("disable_url_normalization", false), } return repo, repo.Id(), nil } diff --git a/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_huggingfaceml_repository.go b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_huggingfaceml_repository.go new file mode 100644 index 000000000..c9bcb612e --- /dev/null +++ b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_huggingfaceml_repository.go @@ -0,0 +1,57 @@ +package remote + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/jfrog/terraform-provider-artifactory/v9/pkg/artifactory/resource/repository" + "github.com/jfrog/terraform-provider-shared/packer" + utilsdk "github.com/jfrog/terraform-provider-shared/util/sdk" +) + +const HuggingFacePackageType = "huggingfaceml" + +type HuggingFaceRepo struct { + RepositoryRemoteBaseParams +} + +var HuggingFaceSchema = func(isResource bool) map[string]*schema.Schema { + return utilsdk.MergeMaps( + BaseRemoteRepoSchema(isResource), + map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Optional: true, + Default: "https://huggingface.co", + Description: "The remote repo URL. Default to 'https://huggingface.co'", + }, + }, + repository.RepoLayoutRefSchema(rclass, HuggingFacePackageType), + ) +} + +func ResourceArtifactoryRemoteHuggingFaceRepository() *schema.Resource { + var unpackRepo = func(s *schema.ResourceData) (interface{}, string, error) { + repo := HuggingFaceRepo{ + RepositoryRemoteBaseParams: UnpackBaseRemoteRepo(s, HuggingFacePackageType), + } + return repo, repo.Id(), nil + } + + constructor := func() (interface{}, error) { + repoLayout, err := repository.GetDefaultRepoLayoutRef(rclass, HuggingFacePackageType)() + if err != nil { + return nil, err + } + + return &HuggingFaceRepo{ + RepositoryRemoteBaseParams: RepositoryRemoteBaseParams{ + Rclass: rclass, + PackageType: HuggingFacePackageType, + RepoLayoutRef: repoLayout.(string), + }, + }, nil + } + + huggingFaceSchema := HuggingFaceSchema(true) + + return mkResourceSchema(huggingFaceSchema, packer.Default(huggingFaceSchema), unpackRepo, constructor) +} diff --git a/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_repository_test.go b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_repository_test.go index 40a9b5179..d79ff000a 100644 --- a/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_repository_test.go +++ b/pkg/artifactory/resource/repository/remote/resource_artifactory_remote_repository_test.go @@ -67,7 +67,7 @@ func TestAccRemoteUpgradeFromVersionWithNoDisableProxyAttr(t *testing.T) { } func TestAccRemoteAllowDotsUnderscorersAndDashesInKeyGH129(t *testing.T) { - _, fqrn, name := testutil.MkNames("terraform-local-test-repo-basic", "artifactory_remote_debian_repository") + _, fqrn, name := testutil.MkNames("local-test-repo-basic", "artifactory_remote_debian_repository") key := fmt.Sprintf("debian-remote.teleport_%d", testutil.RandomInt()) remoteRepositoryBasic := fmt.Sprintf(` @@ -105,7 +105,7 @@ func TestAccRemoteAllowDotsUnderscorersAndDashesInKeyGH129(t *testing.T) { func TestAccRemoteKeyHasSpecialCharsFails(t *testing.T) { const failKey = ` - resource "artifactory_remote_npm_repository" "terraform-remote-test-repo-basic" { + resource "artifactory_remote_npm_repository" "remote-test-repo-basic" { key = "IHave++special,Chars" url = "https://registry.npmjs.org/" repo_layout_ref = "npm-default" @@ -165,7 +165,7 @@ func TestAccRemoteDockerRepositoryDepFalse(t *testing.T) { func TestAccRemoteDockerRepositoryDependenciesTrueEmptyListFails(t *testing.T) { const failKey = ` - resource "artifactory_remote_docker_repository" "terraform-remote-docker-repo-basic" { + resource "artifactory_remote_docker_repository" "remote-docker-repo-basic" { key = "remote-docker" url = "https://registry.npmjs.org/" retrieval_cache_period_seconds = 70 @@ -190,7 +190,7 @@ func TestAccRemoteDockerRepositoryDependenciesTrueEmptyListFails(t *testing.T) { func TestAccRemoteDockerRepositoryDepListEmptyStringFails(t *testing.T) { const failKey = ` - resource "artifactory_remote_docker_repository" "terraform-remote-docker-repo-basic" { + resource "artifactory_remote_docker_repository" "remote-docker-repo-basic" { key = "remote-docker" url = "https://registry.npmjs.org/" retrieval_cache_period_seconds = 70 @@ -230,7 +230,6 @@ func TestAccRemoteDockerRepoUpdate(t *testing.T) { "username": "admin", "password": "password1", "xray_index": "false", - "disable_url_normalization": "false", } var testDataUpdated = map[string]string{ "resource_name": name, @@ -246,7 +245,6 @@ func TestAccRemoteDockerRepoUpdate(t *testing.T) { "username": "admin1", "password": "password", "xray_index": "true", - "disable_url_normalization": "true", } resource.Test(t, resource.TestCase{ @@ -292,7 +290,6 @@ resource "artifactory_remote_docker_repository" "{{ .resource_name }}" { username = "{{ .username }}" password = "{{ .password }}" xray_index = {{ .xray_index }} - disable_url_normalization = {{ .disable_url_normalization }} } ` @@ -309,7 +306,8 @@ func verifyRepository(fqrn string, testData map[string]string) resource.TestChec resource.TestCheckResourceAttr(fqrn, "proxy", testData["proxy"]), resource.TestCheckResourceAttr(fqrn, "username", testData["username"]), resource.TestCheckResourceAttr(fqrn, "xray_index", testData["xray_index"]), - resource.TestCheckResourceAttr(fqrn, "disable_url_normalization", testData["disable_url_normalization"]), + resource.TestCheckResourceAttr(fqrn, "property_sets.#", "1"), + resource.TestCheckResourceAttr(fqrn, "property_sets.0", "artifactory"), ) } @@ -431,6 +429,37 @@ func TestAccRemoteHelmRepositoryWithAdditionalCheckFunctions(t *testing.T) { })) } +func TestAccRemoteHuggingFaceRepository(t *testing.T) { + _, fqrn, name := testutil.MkNames("local-test-repo-huggingfaceml", "artifactory_remote_huggingfaceml_repository") + + remoteRepositoryBasic := fmt.Sprintf(` + resource "artifactory_remote_huggingfaceml_repository" "%s" { + key = "%s" + } + `, name, name) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: acctest.VerifyDeleted(fqrn, acctest.CheckRepo), + Steps: []resource.TestStep{ + { + Config: remoteRepositoryBasic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(fqrn, "key", name), + resource.TestCheckResourceAttr(fqrn, "url", "https://huggingface.co"), + ), + }, + { + ResourceName: fqrn, + ImportState: true, + ImportStateVerify: true, + ImportStateCheck: validator.CheckImportState(name, "key"), + }, + }, + }) +} + func TestAccRemoteNpmRepository(t *testing.T) { const packageType = "npm" resource.Test(mkNewRemoteTestCase(packageType, t, map[string]interface{}{ @@ -720,7 +749,7 @@ func TestAccRemoteRepositoryChangeConfigGH148(t *testing.T) { func TestAccRemoteRepository_basic(t *testing.T) { id := rand.Int() - name := fmt.Sprintf("terraform-remote-test-repo-basic%d", id) + name := fmt.Sprintf("remote-test-repo-basic%d", id) fqrn := fmt.Sprintf("artifactory_remote_npm_repository.%s", name) const remoteRepoBasic = ` resource "artifactory_remote_npm_repository" "%s" { @@ -769,7 +798,7 @@ func TestAccRemoteRepository_nugetNew(t *testing.T) { } ` id := testutil.RandomInt() - name := fmt.Sprintf("terraform-remote-test-repo-nuget%d", id) + name := fmt.Sprintf("remote-test-repo-nuget%d", id) fqrn := fmt.Sprintf("artifactory_remote_nuget_repository.%s", name) resource.Test(t, resource.TestCase{ @@ -837,8 +866,9 @@ func mkNewRemoteTestCase(repoType string, t *testing.T, extraFields map[string]i "content_synchronisation": map[string]interface{}{ "enabled": false, // even when set to true, it seems to come back as false on the wire }, - "download_direct": true, - "cdn_redirect": false, // even when set to true, it comes back as false on the wire (presumably unless testing against a cloud platform) + "download_direct": true, + "cdn_redirect": false, // even when set to true, it comes back as false on the wire (presumably unless testing against a cloud platform) + "disable_url_normalization": true, } allFields := utilsdk.MergeMaps(defaultFields, extraFields) allFieldsHcl := utilsdk.FmtMapToHcl(allFields) @@ -922,7 +952,7 @@ func deleteTestCertificate(t *testing.T, certificateAlias string, certificateEnd } func mkRemoteTestCaseWithAdditionalCheckFunctions(repoType string, t *testing.T, extraFields map[string]interface{}) (*testing.T, resource.TestCase) { - _, fqrn, name := testutil.MkNames("terraform-remote-test-repo-full", fmt.Sprintf("artifactory_remote_%s_repository", repoType)) + _, fqrn, name := testutil.MkNames("remote-test-repo-full", fmt.Sprintf("artifactory_remote_%s_repository", repoType)) defaultFields := map[string]interface{}{ "key": name, @@ -959,6 +989,7 @@ func mkRemoteTestCaseWithAdditionalCheckFunctions(repoType string, t *testing.T, "content_synchronisation": map[string]interface{}{ "enabled": false, // even when set to true, it seems to come back as false on the wire }, + "disable_url_normalization": true, } allFields := utilsdk.MergeMaps(defaultFields, extraFields) allFieldsHcl := utilsdk.FmtMapToHcl(allFields) @@ -1010,7 +1041,7 @@ func TestAccRemoteRepository_generic_with_propagate(t *testing.T) { } ` id := testutil.RandomInt() - name := fmt.Sprintf("terraform-remote-test-repo-basic%d", id) + name := fmt.Sprintf("remote-test-repo-basic%d", id) fqrn := fmt.Sprintf("artifactory_remote_generic_repository.%s", name) resource.Test(t, resource.TestCase{ @@ -1048,7 +1079,7 @@ func TestAccRemoteRepository_gems_with_propagate_fails(t *testing.T) { } ` id := testutil.RandomInt() - name := fmt.Sprintf("terraform-remote-test-repo-basic%d", id) + name := fmt.Sprintf("remote-test-repo-basic%d", id) fqrn := fmt.Sprintf("artifactory_remote_gems_repository.%s", name) resource.Test(t, resource.TestCase{ @@ -1112,7 +1143,7 @@ func TestRemoteMavenRepoResourceStateUpgradeV1(t *testing.T) { // https://github.com/jfrog/terraform-provider-artifactory/issues/225 func TestAccRemoteRepository_MissedRetrievalCachePeriodSecs_retained_between_updates_GH225(t *testing.T) { - _, fqrn, name := testutil.MkNames("terraform-remote-test-cran-remote-", "artifactory_remote_cran_repository") + _, fqrn, name := testutil.MkNames("remote-test-cran-remote-", "artifactory_remote_cran_repository") remoteRepositoryInit := fmt.Sprintf(` resource "artifactory_remote_cran_repository" "%s" { @@ -1172,7 +1203,7 @@ func TestAccRemoteRepository_MissedRetrievalCachePeriodSecs_retained_between_upd } func TestAccRemoteRepository_AttemptToRemoveRemoteRepoLayout_GH746(t *testing.T) { - _, fqrn, name := testutil.MkNames("terraform-remote-test-cran-remote-", "artifactory_remote_cran_repository") + _, fqrn, name := testutil.MkNames("remote-test-cran-remote-", "artifactory_remote_cran_repository") remoteRepositoryInit := fmt.Sprintf(` resource "artifactory_remote_cran_repository" "%s" { @@ -1248,7 +1279,7 @@ func TestAccRemoteRepository_AttemptToRemoveRemoteRepoLayout_GH746(t *testing.T) // https://github.com/jfrog/terraform-provider-artifactory/issues/241 func TestAccRemoteRepository_assumed_offline_period_secs_has_default_value_GH241(t *testing.T) { - _, fqrn, name := testutil.MkNames("terraform-remote-test-repo-docker", "artifactory_remote_docker_repository") + _, fqrn, name := testutil.MkNames("remote-test-repo-docker", "artifactory_remote_docker_repository") remoteRepositoryInit := fmt.Sprintf(` resource "artifactory_remote_docker_repository" "%s" { @@ -1286,7 +1317,7 @@ func TestAccRemoteRepository_assumed_offline_period_secs_has_default_value_GH241 } func TestAccRemoteProxyUpdateGH2(t *testing.T) { - _, fqrn, name := testutil.MkNames("terraform-remote-test-go-remote-proxy-", "artifactory_remote_go_repository") + _, fqrn, name := testutil.MkNames("remote-test-go-remote-proxy-", "artifactory_remote_go_repository") fakeProxy := "test-proxy" diff --git a/pkg/artifactory/resource/repository/repository.go b/pkg/artifactory/resource/repository/repository.go index 9b188a562..7552f8cc9 100644 --- a/pkg/artifactory/resource/repository/repository.go +++ b/pkg/artifactory/resource/repository/repository.go @@ -254,38 +254,6 @@ var RepoKeyValidator = validation.All( validation.StringDoesNotContainAny(" !@#$%^&*()+={}[]:;<>,/?~`|\\"), ) -var RepoTypesSupported = []string{ - "alpine", - "bower", - "cargo", - "chef", - "cocoapods", - "composer", - "conan", - "conda", - "cran", - "debian", - "docker", - "gems", - "generic", - "gitlfs", - "go", - "gradle", - "helm", - "ivy", - "maven", - "npm", - "nuget", - "opkg", - "p2", - "puppet", - "pypi", - "rpm", - "sbt", - "vagrant", - "vcs", -} - var GradleLikePackageTypes = []string{ "gradle", "sbt",