Skip to content

Commit

Permalink
flush out resource client a bit more
Browse files Browse the repository at this point in the history
  • Loading branch information
sam-heilbron committed Apr 12, 2023
1 parent d7a41ce commit 3a05175
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 18 deletions.
30 changes: 30 additions & 0 deletions pkg/api/v1/clients/vault/converters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package vault

import (
"context"
"fmt"

"github.com/solo-io/solo-kit/pkg/api/v1/resources"
)

var _ error = new(UnrecoverableConversionError)

type SecretConverter interface {
// FromSecret accepts the raw value of a Vault secret and returns the Gloo representation
// of that secret. An error is returned if the conversion failed
FromSecret(ctx context.Context, secret Secret) (resources.Resource, error)
}

type UnrecoverableConversionError struct {
Err error
}

func UnrecoverableConversionErr(err error) UnrecoverableConversionError {
return UnrecoverableConversionError{
Err: err,
}
}

func (u UnrecoverableConversionError) Error() string {
return fmt.Sprintf("UnrecoverableConversionError: %v", u.Err)
}
115 changes: 97 additions & 18 deletions pkg/api/v1/clients/vault/pki_resource_client.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,55 @@
package vault

import (
"context"
"errors"
"sort"
"time"

"github.com/solo-io/solo-kit/pkg/api/shared"
"github.com/hashicorp/vault/api"
"github.com/solo-io/go-utils/contextutils"

"github.com/solo-io/solo-kit/pkg/api/v1/clients"
"github.com/solo-io/solo-kit/pkg/api/v1/resources"
)

/**
Open Questions:
- Namespace concept is not available in Vault. How do production users separate certs or identify groupings? Will we just list all?
- How does rotation work? Do we need to worry about that? I imagine we don't, as we will just reference a cert by ref
- Secrets in Gloo have a name/namespace concept. We need some clear mapping of cert to name/namespace
TODO:
- Query Certificate API instead of returning empty list
- Determine how PrivateKeys will be pulled and if they are necessary
- Standup integration tests
*/

var _ clients.ResourceClient = new(PkiResourceClient)

type PkiResourceClient struct {
//vault *api.Client
var (
readOnlyError = errors.New("PKI ResourceClient is read-only")
notImplementedError = errors.New("PKI ResourceClient is a WIP and this feature is not yet implemented")
)

type PkiResourceClient struct {
resourceType resources.VersionedResource

client *api.Client
secretConverter SecretConverter
}

type PKIResourceClientOptions struct {
Client *api.Client
SecretConverter SecretConverter
}

func NewPkiResourceClient(resourceType resources.VersionedResource, options PKIResourceClientOptions) *PkiResourceClient {
return &PkiResourceClient{
resourceType: resourceType,
client: options.Client,
secretConverter: options.SecretConverter,
}
}

func (p PkiResourceClient) Kind() string {
Expand All @@ -24,32 +60,60 @@ func (p PkiResourceClient) NewResource() resources.Resource {
return resources.Clone(p.resourceType)
}

// Register is a no-op.
// Deprecated: As outlined in the ResourceClient interface,
// Register is only necessary for the kubernetes resource client
func (p PkiResourceClient) Register() error {
return nil
}

func (p PkiResourceClient) Read(namespace, name string, opts clients.ReadOpts) (resources.Resource, error) {
//TODO implement me
panic("implement me")
panic(notImplementedError)
}

func (p PkiResourceClient) Write(resource resources.Resource, opts clients.WriteOpts) (resources.Resource, error) {
//TODO implement me
panic("implement me")
}
func (p PkiResourceClient) List(namespace string, opts clients.ListOpts) (resources.ResourceList, error) {
// 1. Extract the secrets from the Vault store
vaultSecretList, err := p.listSecrets(opts.Ctx)
if err != nil {
return nil, err
}

func (p PkiResourceClient) Delete(namespace, name string, opts clients.DeleteOpts) error {
//TODO implement me
panic("implement me")
// 2. Convert the secrets to the Gloo resource format
resourceList, conversionErr := p.convertSecrets(opts.Ctx, vaultSecretList)
if conversionErr != nil {
return nil, conversionErr
}

// 3. Sort the resources for idempotence
sort.SliceStable(resourceList, func(i, j int) bool {
return resourceList[i].GetMetadata().Name < resourceList[j].GetMetadata().Name
})
return resourceList, nil
}

func (p PkiResourceClient) List(namespace string, opts clients.ListOpts) (resources.ResourceList, error) {
//TODO implement me
panic("implement me")
func (p PkiResourceClient) listSecrets(ctx context.Context) (SecretList, error) {
return SecretList{}, nil
}

func (p PkiResourceClient) ApplyStatus(statusClient resources.StatusClient, inputResource resources.InputResource, opts clients.ApplyStatusOpts) (resources.Resource, error) {
return shared.ApplyStatus(p, statusClient, inputResource, opts)
func (p PkiResourceClient) convertSecrets(ctx context.Context, vaultSecretList SecretList) (resources.ResourceList, error) {
var resourceList resources.ResourceList
for _, vaultSecret := range vaultSecretList {
resource, conversionErr := p.secretConverter.FromSecret(ctx, vaultSecret)
if conversionErr != nil {
switch _ := conversionErr.(type) {
case *UnrecoverableConversionError:
// This should rarely (if ever) be used
// Ideally invalid secrets do not half execution, and instead are processed
return nil, conversionErr
default:
contextutils.LoggerFrom(ctx).Warnf("Failed to convert VaultSecret to GlooSecret: %v", conversionErr)
continue
}
}
resourceList = append(resourceList, resource)
}

return resourceList, nil
}

func (p PkiResourceClient) Watch(namespace string, opts clients.WatchOpts) (<-chan resources.ResourceList, <-chan error, error) {
Expand All @@ -58,7 +122,10 @@ func (p PkiResourceClient) Watch(namespace string, opts clients.WatchOpts) (<-ch
errs := make(chan error)

listOpts := clients.ListOpts{
Ctx: opts.Ctx,
Ctx: opts.Ctx,

// These advanced selectors are available to resources which support label selection
// This is not supported for Vault
Selector: opts.Selector,
ExpressionSelector: opts.ExpressionSelector,
}
Expand Down Expand Up @@ -89,3 +156,15 @@ func (p PkiResourceClient) Watch(namespace string, opts clients.WatchOpts) (<-ch

return resourcesChan, errs, nil
}

func (p PkiResourceClient) Write(resource resources.Resource, opts clients.WriteOpts) (resources.Resource, error) {
panic(readOnlyError)
}

func (p PkiResourceClient) Delete(namespace, name string, opts clients.DeleteOpts) error {
panic(readOnlyError)
}

func (p PkiResourceClient) ApplyStatus(statusClient resources.StatusClient, inputResource resources.InputResource, opts clients.ApplyStatusOpts) (resources.Resource, error) {
panic(readOnlyError)
}
11 changes: 11 additions & 0 deletions pkg/api/v1/clients/vault/resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vault

// Resources represents the raw data models returned by the Vault API

type Certificate map[string]string

type Secret struct {
Certificate
}

type SecretList []Secret

0 comments on commit 3a05175

Please sign in to comment.