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

Add List() to GPG Keys, ProviderBinaryUploaded to registry platforms #602

Merged
merged 3 commits into from Dec 8, 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
Expand Up @@ -3,6 +3,8 @@
## Bug Fixes

## Enhancements
* Adds `List()` method to `GPGKeys` interface by @sebasslash [#602](https://github.com/hashicorp/go-tfe/pull/602)
* Adds `ProviderBinaryUploaded` field to `RegistryPlatforms` struct by @sebasslash [#602](https://github.com/hashicorp/go-tfe/pull/602)

# v1.15.0

Expand Down
52 changes: 52 additions & 0 deletions gpg_key.go
Expand Up @@ -15,6 +15,9 @@ var _ GPGKeys = (*gpgKeys)(nil)
//
// TFE API Docs: https://www.terraform.io/cloud-docs/api-docs/private-registry/gpg-keys
type GPGKeys interface {
// Lists GPG keys in a private registry.
ListPrivate(ctx context.Context, options GPGKeyListOptions) (*GPGKeyList, error)

// Uploads a GPG Key to a private registry scoped with a namespace.
Create(ctx context.Context, registryName RegistryName, options GPGKeyCreateOptions) (*GPGKey, error)

Expand All @@ -33,6 +36,12 @@ type gpgKeys struct {
client *Client
}

// GPGKeyList represents a list of GPG keys.
type GPGKeyList struct {
*Pagination
Items []*GPGKey
}

// GPGKey represents a signed GPG key for a TFC/E private provider.
type GPGKey struct {
ID string `jsonapi:"primary,gpg-keys"`
Expand All @@ -53,6 +62,14 @@ type GPGKeyID struct {
KeyID string
}

// GPGKeyListOptions represents all the available options to list keys in a registry.
type GPGKeyListOptions struct {
ListOptions

// Required: A list of one or more namespaces. Must be authorized TFC/E organization names.
Namespaces []string `url:"filter[namespace]"`
}

// GPGKeyCreateOptions represents all the available options used to create a GPG key.
type GPGKeyCreateOptions struct {
Type string `jsonapi:"primary,gpg-keys"`
Expand All @@ -66,6 +83,27 @@ type GPGKeyUpdateOptions struct {
Namespace string `jsonapi:"attr,namespace"`
}

// ListPrivate lists the private registry GPG keys for specified namespaces.
func (s *gpgKeys) ListPrivate(ctx context.Context, options GPGKeyListOptions) (*GPGKeyList, error) {
if err := options.valid(); err != nil {
return nil, err
}

u := fmt.Sprintf("/api/registry/%s/v2/gpg-keys", url.QueryEscape(string(PrivateRegistry)))
req, err := s.client.NewRequest("GET", u, &options)
if err != nil {
return nil, err
}

keyl := &GPGKeyList{}
err = req.Do(ctx, keyl)
if err != nil {
return nil, err
}

return keyl, nil
}

func (s *gpgKeys) Create(ctx context.Context, registryName RegistryName, options GPGKeyCreateOptions) (*GPGKey, error) {
if err := options.valid(); err != nil {
return nil, err
Expand Down Expand Up @@ -179,6 +217,20 @@ func (o GPGKeyID) valid() error {
return nil
}

func (o *GPGKeyListOptions) valid() error {
if len(o.Namespaces) == 0 {
return ErrInvalidNamespace
}

for _, namespace := range o.Namespaces {
if namespace == "" || !validString(&namespace) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validString already checks for nil or empty string

return ErrInvalidNamespace
}
}

return nil
}

func (o GPGKeyCreateOptions) valid() error {
if !validString(&o.Namespace) {
return ErrInvalidNamespace
Expand Down
85 changes: 85 additions & 0 deletions gpg_key_integration_test.go
Expand Up @@ -8,6 +8,91 @@ import (
"github.com/stretchr/testify/require"
)

func TestGPGKeyList(t *testing.T) {
client := testClient(t)
ctx := context.Background()

org1, org1Cleanup := createOrganization(t, client)
t.Cleanup(org1Cleanup)

org2, org2Cleanup := createOrganization(t, client)
t.Cleanup(org2Cleanup)

upgradeOrganizationSubscription(t, client, org1)
upgradeOrganizationSubscription(t, client, org2)

provider1, provider1Cleanup := createRegistryProvider(t, client, org1, PrivateRegistry)
t.Cleanup(provider1Cleanup)

provider2, provider2Cleanup := createRegistryProvider(t, client, org2, PrivateRegistry)
t.Cleanup(provider2Cleanup)

gpgKey1, gpgKey1Cleanup := createGPGKey(t, client, org1, provider1)
t.Cleanup(gpgKey1Cleanup)

gpgKey2, gpgKey2Cleanup := createGPGKey(t, client, org2, provider2)
t.Cleanup(gpgKey2Cleanup)

t.Run("with single namespace", func(t *testing.T) {
opts := GPGKeyListOptions{
Namespaces: []string{org1.Name},
}

keyl, err := client.GPGKeys.ListPrivate(ctx, opts)
require.NoError(t, err)

require.Len(t, keyl.Items, 1)
assert.Equal(t, gpgKey1.ID, keyl.Items[0].ID)
assert.Equal(t, gpgKey1.KeyID, keyl.Items[0].KeyID)
})

t.Run("with multiple namespaces", func(t *testing.T) {
t.Skip("Skipping due to GPG Key API not returning keys for multiple namespaces")

opts := GPGKeyListOptions{
Namespaces: []string{org1.Name, org2.Name},
}

keyl, err := client.GPGKeys.ListPrivate(ctx, opts)
require.NoError(t, err)

require.Len(t, keyl.Items, 2)
for i, key := range []*GPGKey{
gpgKey1,
gpgKey2,
} {
assert.Equal(t, key.ID, keyl.Items[i].ID)
assert.Equal(t, key.KeyID, keyl.Items[i].KeyID)
}
})

t.Run("with list options", func(t *testing.T) {
opts := GPGKeyListOptions{
Namespaces: []string{org1.Name},
ListOptions: ListOptions{
PageNumber: 999,
PageSize: 100,
},
}

keyl, err := client.GPGKeys.ListPrivate(ctx, opts)
require.NoError(t, err)
require.Empty(t, keyl.Items)
assert.Equal(t, 999, keyl.CurrentPage)
assert.Equal(t, 1, keyl.TotalCount)
})

t.Run("with invalid options", func(t *testing.T) {
t.Run("invalid namespace", func(t *testing.T) {
opts := GPGKeyListOptions{
Namespaces: []string{},
}
_, err := client.GPGKeys.ListPrivate(ctx, opts)
require.EqualError(t, err, ErrInvalidNamespace.Error())
})
})
}

func TestGPGKeyCreate(t *testing.T) {
client := testClient(t)
ctx := context.Background()
Expand Down
15 changes: 15 additions & 0 deletions mocks/gpg_key_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions registry_provider_platform.go
Expand Up @@ -33,11 +33,12 @@ type registryProviderPlatforms struct {

// RegistryProviderPlatform represents a registry provider platform
type RegistryProviderPlatform struct {
ID string `jsonapi:"primary,registry-provider-platforms"`
OS string `jsonapi:"attr,os"`
Arch string `jsonapi:"attr,arch"`
Filename string `jsonapi:"attr,filename"`
Shasum string `jsonapi:"attr,shasum"`
ID string `jsonapi:"primary,registry-provider-platforms"`
OS string `jsonapi:"attr,os"`
Arch string `jsonapi:"attr,arch"`
Filename string `jsonapi:"attr,filename"`
Shasum string `jsonapi:"attr,shasum"`
ProviderBinaryUploaded bool `jsonapi:"attr,provider-binary-uploaded"`

// Relations
RegistryProviderVersion *RegistryProviderVersion `jsonapi:"relation,registry-provider-version"`
Expand Down
2 changes: 2 additions & 0 deletions registry_provider_platform_integration_test.go
Expand Up @@ -43,6 +43,7 @@ func TestRegistryProviderPlatformsCreate(t *testing.T) {
assert.Equal(t, options.Arch, rpp.Arch)
assert.Equal(t, options.Shasum, rpp.Shasum)
assert.Equal(t, options.Filename, rpp.Filename)
assert.False(t, rpp.ProviderBinaryUploaded)

t.Run("relationships are properly decoded", func(t *testing.T) {
assert.Equal(t, version.ID, rpp.RegistryProviderVersion.ID)
Expand Down Expand Up @@ -245,6 +246,7 @@ func TestRegistryProviderPlatformsRead(t *testing.T) {
assert.Equal(t, platformID.Arch, readPlatform.Arch)
assert.Equal(t, platform.Filename, readPlatform.Filename)
assert.Equal(t, platform.Shasum, readPlatform.Shasum)
assert.Equal(t, platform.ProviderBinaryUploaded, readPlatform.ProviderBinaryUploaded)

t.Run("relationships are properly decoded", func(t *testing.T) {
assert.Equal(t, platform.RegistryProviderVersion.ID, readPlatform.RegistryProviderVersion.ID)
Expand Down