Skip to content

Commit

Permalink
Sync from server repo (cffda967bdb)
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Spilchen committed Jan 2, 2024
1 parent 41a8a4e commit 7c04c63
Show file tree
Hide file tree
Showing 12 changed files with 315 additions and 82 deletions.
66 changes: 36 additions & 30 deletions commands/cmd_scrutinize.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ import (
"os"
"strings"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/apimachinery/pkg/types"

"github.com/vertica/vcluster/vclusterops"
"github.com/vertica/vcluster/vclusterops/util"
"github.com/vertica/vcluster/vclusterops/vlog"
"github.com/vertica/vertica-kubernetes/pkg/secrets"
)

const (
Expand All @@ -51,11 +50,13 @@ const (

// secretRetriever is an interface for retrieving secrets.
type secretRetriever interface {
RetrieveSecret(namespace, secretName string) ([]byte, []byte, []byte, error)
RetrieveSecret(logger vlog.Printer, namespace, secretName string) ([]byte, []byte, []byte, error)
}

// k8sSecretRetrieverStruct is an implementation of secretRetriever.
type k8sSecretRetrieverStruct struct {
// secretStoreRetrieverStruct is an implementation of secretRetriever. It
// handles reading secrets from k8s and external sources like GSM, AWS, etc.
type secretStoreRetrieverStruct struct {
Log vlog.Printer
}

/* CmdScrutinize
Expand All @@ -68,15 +69,15 @@ type k8sSecretRetrieverStruct struct {
*/
type CmdScrutinize struct {
CmdBase
k8secretRetreiver secretRetriever
sOptions vclusterops.VScrutinizeOptions
secretStoreRetriever secretRetriever
sOptions vclusterops.VScrutinizeOptions
}

func makeCmdScrutinize() *CmdScrutinize {
newCmd := &CmdScrutinize{}
newCmd.parser = flag.NewFlagSet("scrutinize", flag.ExitOnError)
newCmd.sOptions = vclusterops.VScrutinizOptionsFactory()
newCmd.k8secretRetreiver = k8sSecretRetrieverStruct{}
newCmd.secretStoreRetriever = secretStoreRetrieverStruct{}
// required flags
newCmd.sOptions.DBName = newCmd.parser.String("db-name", "", "The name of the database to run scrutinize. May be omitted on k8s.")

Expand Down Expand Up @@ -194,35 +195,40 @@ func (c *CmdScrutinize) Run(vcc vclusterops.VClusterCommands) error {
return err
}

// RetrieveSecret retrieves a secret from Kubernetes and returns its data.
func (k8sSecretRetrieverStruct) RetrieveSecret(namespace, secretName string) (ca, cert, key []byte, err error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, nil, nil, err
// RetrieveSecret retrieves a secret from a secret store, such as Kubernetes or
// GSM, and returns its data.
func (k secretStoreRetrieverStruct) RetrieveSecret(logger vlog.Printer, namespace, secretName string) (ca, cert, key []byte, err error) {
// We use MultiSourceSecretFetcher since it will use the correct client
// depending on the secret path reference of the secret name. This can
// handle reading clients from the k8s-apiserver using a k8s client, or from
// external sources such as Google Secret Manager (GSM).
fetcher := secrets.MultiSourceSecretFetcher{
Log: &logger,
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, nil, nil, err
ctx := context.Background()
var certData map[string][]byte
fetchName := types.NamespacedName{
Namespace: namespace,
Name: secretName,
}
secretsClient := clientset.CoreV1().Secrets(namespace)
certData, err := secretsClient.Get(context.Background(), secretName, metav1.GetOptions{})
certData, err = fetcher.Fetch(ctx, fetchName)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, fmt.Errorf("failed to fetch secret: %w", err)
}
const (
CACertName = "ca.crt"
TLSCertName = "tls.crt"
TLSKeyName = "tls.key"
)
caCertVal, exists := certData.Data[CACertName]
caCertVal, exists := certData[CACertName]
if !exists {
return nil, nil, nil, fmt.Errorf("missing key %s in secret", CACertName)
}
tlsCertVal, exists := certData.Data[TLSCertName]
tlsCertVal, exists := certData[TLSCertName]
if !exists {
return nil, nil, nil, fmt.Errorf("missing key %s in secret", TLSCertName)
}
tlsKeyVal, exists := certData.Data[TLSKeyName]
tlsKeyVal, exists := certData[TLSKeyName]
if !exists {
return nil, nil, nil, fmt.Errorf("missing key %s in secret", TLSKeyName)
}
Expand All @@ -231,7 +237,7 @@ func (k8sSecretRetrieverStruct) RetrieveSecret(namespace, secretName string) (ca

func (c *CmdScrutinize) readNMACerts(logger vlog.Printer) error {
loaderFuncs := []func(vlog.Printer) (bool, error){
c.nmaCertLookupFromK8sSecret,
c.nmaCertLookupFromSecretStore,
c.nmaCertLookupFromEnv,
}
for _, fnc := range loaderFuncs {
Expand All @@ -244,9 +250,9 @@ func (c *CmdScrutinize) readNMACerts(logger vlog.Printer) error {
return nil
}

// nmaCertLookupFromK8sSecret retrieves PEM-encoded text of CA certs, the server cert, and
// the server key directly from kubernetes secrets.
func (c *CmdScrutinize) nmaCertLookupFromK8sSecret(logger vlog.Printer) (bool, error) {
// nmaCertLookupFromSecretStore retrieves PEM-encoded text of CA certs, the server cert, and
// the server key directly from a secret store.
func (c *CmdScrutinize) nmaCertLookupFromSecretStore(logger vlog.Printer) (bool, error) {
_, portSet := os.LookupEnv(kubernetesPort)
if !portSet {
return false, nil
Expand All @@ -268,12 +274,12 @@ func (c *CmdScrutinize) nmaCertLookupFromK8sSecret(logger vlog.Printer) (bool, e
return false, nil
}

caCert, cert, key, err := c.k8secretRetreiver.RetrieveSecret(secretNameSpace, secretName)
caCert, cert, key, err := c.secretStoreRetriever.RetrieveSecret(logger, secretNameSpace, secretName)
if err != nil {
return false, fmt.Errorf("failed to read certs from k8s secret %s in namespace %s: %w", secretName, secretNameSpace, err)
return false, fmt.Errorf("failed to read certs from secret store with name %s in namespace %s: %w", secretName, secretNameSpace, err)
}
if len(caCert) != 0 && len(cert) != 0 && len(key) != 0 {
logger.Info("Successfully read cert from k8s secret ", "secretName", secretName, "secretNameSpace", secretNameSpace)
logger.Info("Successfully read cert from secret store", "secretName", secretName, "secretNameSpace", secretNameSpace)
} else {
return false, fmt.Errorf("failed to read CA, cert or key (sizes = %d/%d/%d)",
len(caCert), len(cert), len(key))
Expand Down
23 changes: 15 additions & 8 deletions commands/scrutinize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
package commands

import (
"context"
"errors"
"os"
"testing"

"github.com/vertica/vcluster/vclusterops/vlog"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"

"github.com/stretchr/testify/assert"
)
Expand All @@ -37,8 +40,12 @@ type TestK8sSecretRetriever struct {
ca, cert, key string
}

func (t TestK8sSecretRetriever) GetSecret(_ context.Context, _ types.NamespacedName) (*corev1.Secret, error) {
return nil, nil
}

// RetrieveSecret retrieves a secret and returns mock values.
func (t TestK8sSecretRetriever) RetrieveSecret(_, _ string) (caBytes []byte, certBytes []byte,
func (t TestK8sSecretRetriever) RetrieveSecret(_ vlog.Printer, _, _ string) (caBytes []byte, certBytes []byte,
keyBytes []byte, err error) {
if !t.success { // Allow for dependency injection
return nil, nil, nil, errors.New("failed to retrieve secrets")
Expand Down Expand Up @@ -78,7 +85,7 @@ func TestScrutinCmd(t *testing.T) {
func TestNMACertLookupFromK8sSecret(t *testing.T) {
const randomBytes = "123"
c := makeCmdScrutinize()
c.k8secretRetreiver = TestK8sSecretRetriever{
c.secretStoreRetriever = TestK8sSecretRetriever{
success: true,
ca: "test cert 1",
cert: "test cert 2",
Expand All @@ -92,7 +99,7 @@ func TestNMACertLookupFromK8sSecret(t *testing.T) {

// Case 2: when the certs are configured correctly

ok, err := c.nmaCertLookupFromK8sSecret(vlog.Printer{})
ok, err := c.nmaCertLookupFromSecretStore(vlog.Printer{})
assert.NoError(t, err)
assert.True(t, ok)
assert.Equal(t, "test cert 1", c.sOptions.CaCert)
Expand All @@ -101,28 +108,28 @@ func TestNMACertLookupFromK8sSecret(t *testing.T) {

// If some of the keys are missing
c = makeCmdScrutinize()
c.k8secretRetreiver = TestK8sSecretRetriever{
c.secretStoreRetriever = TestK8sSecretRetriever{
success: true,
ca: "test cert 1",
cert: "test cert 2",
key: "", // Missing
}
ok, err = c.nmaCertLookupFromK8sSecret(vlog.Printer{})
ok, err = c.nmaCertLookupFromSecretStore(vlog.Printer{})
assert.Error(t, err)
assert.False(t, ok)

// Failure to retrieve the secret should fail the request
c = makeCmdScrutinize()
c.k8secretRetreiver = TestK8sSecretRetriever{success: false}
ok, err = c.nmaCertLookupFromK8sSecret(vlog.Printer{})
c.secretStoreRetriever = TestK8sSecretRetriever{success: false}
ok, err = c.nmaCertLookupFromSecretStore(vlog.Printer{})
assert.Error(t, err)
assert.False(t, ok)

// If the nma env vars aren't set, then we go onto the next retrieval method
os.Clearenv()
os.Setenv("KUBERNETES_PORT", randomBytes)
c = makeCmdScrutinize()
ok, err = c.nmaCertLookupFromK8sSecret(vlog.Printer{})
ok, err = c.nmaCertLookupFromSecretStore(vlog.Printer{})
assert.NoError(t, err)
assert.False(t, ok)
}
Expand Down
27 changes: 23 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,62 @@ require (
github.com/go-logr/zapr v1.2.4
github.com/stretchr/testify v1.8.2
github.com/tonglil/buflogr v1.0.1
github.com/vertica/vertica-kubernetes v1.11.3-0.20231219223702-0400ddd35831
go.uber.org/zap v1.25.0
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea
golang.org/x/sys v0.15.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.26.2
k8s.io/apimachinery v0.26.2
k8s.io/client-go v0.26.2
)

require (
cloud.google.com/go/compute v1.21.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
cloud.google.com/go/secretmanager v1.11.1 // indirect
github.com/aws/aws-sdk-go v1.49.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.11.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/term v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/api v0.126.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/grpc v1.58.3 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.26.2 // indirect
k8s.io/client-go v0.26.2 // indirect
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
Expand Down
Loading

0 comments on commit 7c04c63

Please sign in to comment.