Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace deprecated GET api for registry modules #464

Merged
merged 4 commits into from Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
@@ -1,8 +1,10 @@
# Unreleased

## Enhancements
* [beta] Add support for triggering Workspace runs through matching Git tags [#434](https://github.com/hashicorp/go-tfe/pull/434)
* Add `Query` param field to `AgentPoolListOptions` to allow searching based on agent pool name, by @JarrettSpiker [#417](https://github.com/hashicorp/go-tfe/pull/417)
* Add organization scope and allowed workspaces field for scope agents by @Netra2104 [#453](https://github.com/hashicorp/go-tfe/pull/453)
* Adds `Namespace` and `RegistryName` fields to `RegistryModuleID` to allow reading of Public Registry Modules by @Uk1288 [#464](https://github.com/hashicorp/go-tfe/pull/464)

## Bug fixes
* Fixed JSON mapping for Configuration Versions failing to properly set the `speculative` property [#459](https://github.com/hashicorp/go-tfe/pull/459)
Expand Down
4 changes: 3 additions & 1 deletion errors.go
Expand Up @@ -174,7 +174,7 @@ var (

ErrInvalidArch = errors.New("invalid value for arch")

ErrInvalidRegistryName = errors.New("invalid value for registry-name")
ErrInvalidRegistryName = errors.New(`invalid value for registry-name. It must be either "private" or "public"`)
)

// Missing values for required field/option
Expand Down Expand Up @@ -300,4 +300,6 @@ var (
ErrRequiredFilename = errors.New("filename is required")

ErrInvalidAsciiArmor = errors.New("ascii armor is invalid")

ErrRequiredNamespace = errors.New("namespace is required for public registry")
)
37 changes: 36 additions & 1 deletion registry_module.go
Expand Up @@ -3,7 +3,9 @@ package tfe
import (
"context"
"fmt"
"log"
"net/url"
"strings"
)

// Compile-time proof of interface implementation.
Expand Down Expand Up @@ -82,6 +84,11 @@ type RegistryModuleID struct {
Name string
// The module's provider, see RegistryModule.Provider
Provider string
// The namespace of the module. For private modules this is the name of the organization that owns the module
// Required for public modules
Namespace string
// Either public or private. If not provided, defaults to private
RegistryName RegistryName
}

// RegistryModuleList represents a list of registry modules.
Expand All @@ -95,6 +102,8 @@ type RegistryModule struct {
ID string `jsonapi:"primary,registry-modules"`
Name string `jsonapi:"attr,name"`
Provider string `jsonapi:"attr,provider"`
RegistryName RegistryName `jsonapi:"attr,registry-name"`
Namespace string `jsonapi:"attr,namespace"`
Permissions *RegistryModulePermissions `jsonapi:"attr,permissions"`
Status RegistryModuleStatus `jsonapi:"attr,status"`
VCSRepo *VCSRepo `jsonapi:"attr,vcs-repo"`
Expand Down Expand Up @@ -307,12 +316,25 @@ func (r *registryModules) Read(ctx context.Context, moduleID RegistryModuleID) (
return nil, err
}

if moduleID.RegistryName == "" {
log.Println("[WARN] Support for using the RegistryModuleID without RegistryName is deprecated as of release 1.5.0 and may be removed in a future version. The preferred method is to include the RegistryName in RegistryModuleID.")
moduleID.RegistryName = PrivateRegistry
}

if moduleID.RegistryName == PrivateRegistry && strings.TrimSpace(moduleID.Namespace) == "" {
log.Println("[WARN] Support for using the RegistryModuleID without Namespace is deprecated as of release 1.5.0 and may be removed in a future version. The preferred method is to include the Namespace in RegistryModuleID.")
moduleID.Namespace = moduleID.Organization
}

u := fmt.Sprintf(
"registry-modules/show/%s/%s/%s",
"organizations/%s/registry-modules/%s/%s/%s/%s",
url.QueryEscape(moduleID.Organization),
url.QueryEscape(string(moduleID.RegistryName)),
url.QueryEscape(moduleID.Namespace),
url.QueryEscape(moduleID.Name),
url.QueryEscape(moduleID.Provider),
)

req, err := r.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
Expand Down Expand Up @@ -420,6 +442,19 @@ func (o RegistryModuleID) valid() error {
return ErrInvalidProvider
}

switch o.RegistryName {
case PublicRegistry:
if !validString(&o.Namespace) {
return ErrRequiredNamespace
}
case PrivateRegistry:
case "":
// no-op: RegistryName is optional
// for all other string
default:
return ErrInvalidRegistryName
}

return nil
}

Expand Down
45 changes: 43 additions & 2 deletions registry_module_integration_test.go
Expand Up @@ -417,6 +417,31 @@ func TestRegistryModulesRead(t *testing.T) {
})
})

t.Run("with complete registry module ID fields", func(t *testing.T) {
rm, err := client.RegistryModules.Read(ctx, RegistryModuleID{
Organization: orgTest.Name,
Name: registryModuleTest.Name,
Provider: registryModuleTest.Provider,
Namespace: orgTest.Name,
RegistryName: PrivateRegistry,
})
require.NoError(t, err)
require.NotEmpty(t, rm)
assert.Equal(t, registryModuleTest.ID, rm.ID)

t.Run("permissions are properly decoded", func(t *testing.T) {
require.NotEmpty(t, rm.Permissions)
assert.True(t, rm.Permissions.CanDelete)
assert.True(t, rm.Permissions.CanResync)
assert.True(t, rm.Permissions.CanRetry)
})

t.Run("timestamps are properly decoded", func(t *testing.T) {
assert.NotEmpty(t, rm.CreatedAt)
assert.NotEmpty(t, rm.UpdatedAt)
})
})

t.Run("without a name", func(t *testing.T) {
rm, err := client.RegistryModules.Read(ctx, RegistryModuleID{
Organization: orgTest.Name,
Expand Down Expand Up @@ -457,6 +482,18 @@ func TestRegistryModulesRead(t *testing.T) {
assert.Equal(t, err, ErrInvalidProvider)
})

t.Run("with an invalid registry name", func(t *testing.T) {
rm, err := client.RegistryModules.Read(ctx, RegistryModuleID{
Organization: orgTest.Name,
Name: registryModuleTest.Name,
Provider: registryModuleTest.Provider,
Namespace: orgTest.Name,
RegistryName: "PRIVATE",
})
assert.Nil(t, rm)
assert.Equal(t, err, ErrInvalidRegistryName)
})

t.Run("without a valid organization", func(t *testing.T) {
rm, err := client.RegistryModules.Read(ctx, RegistryModuleID{
Organization: badIdentifier,
Expand Down Expand Up @@ -775,8 +812,10 @@ func TestRegistryModule_Unmarshal(t *testing.T) {
"type": "registry-modules",
"id": "1",
"attributes": map[string]interface{}{
"name": "module",
"provider": "tfe",
"name": "module",
"provider": "tfe",
"namespace": "org-abc",
"registry-name": "private",
"permissions": map[string]interface{}{
"can-delete": true,
"can-resync": true,
Expand Down Expand Up @@ -815,6 +854,8 @@ func TestRegistryModule_Unmarshal(t *testing.T) {
assert.Equal(t, rm.ID, "1")
assert.Equal(t, rm.Name, "module")
assert.Equal(t, rm.Provider, "tfe")
assert.Equal(t, rm.Namespace, "org-abc")
assert.Equal(t, rm.RegistryName, PrivateRegistry)
assert.Equal(t, rm.Permissions.CanDelete, true)
assert.Equal(t, rm.Permissions.CanRetry, true)
assert.Equal(t, rm.Status, RegistryModuleStatusPending)
Expand Down